<!DOCTYPE html>
<html lang="ko" dir="ltr">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Hooks 最佳实践 – codthing</title>
    


  
  <script defer src="/js/fuse.min.c4bee9a8d44273d6154fe86006923d4131eb2e8069d8897687137c6f37f553e3.js"></script>



<script src="/js/enquire.min.aa37bdcb743826eecdae5c5d177fc7d6552340f1b4378ffaa9c82b2c6111400b.js"></script>

<script defer src="/js/lazysizes.min.5e11d056075a05065b9c0bfec44084a113fc2976c2952ec804dedb61c7662db9.js"></script>

<script defer src="/js/helper/getParents.min.cd44056d552c69703dced61ae60f99166a0ddf44be93d6aa5d5127d98bf98984.js"></script>

<script defer src="/js/helper/fadeinout.min.58d290b487a552ce9c144bf938c2758c4a3afbc4f17d70d11771c9e3c61fe3d0.js"></script>

<script>
  "use strict";

  window.onload = function() {
    
    var languagedir = JSON.parse("\"ltr\"");
    
    var baseurl = JSON.parse("\"https://codthing.github.io/\"");
    
    var permalink = JSON.parse("\"https://codthing.github.io/react/hooks/hooks-bp/\"");

    
    var toggleSidebarElem = document.getElementById("toggle-sidebar");
    var toggleMenuElem = document.getElementById("toggle-menu");
    var tocBodyElem = document.querySelector('.toc__body');
    var tocLabelElem = document.querySelector('.toc__label');
    var listMainElem = document.getElementById('list-main');
    var listSideElem = document.getElementById('list-side');
    var singleMenuElem = document.getElementById('single-menu');
    var sliderIcons = document.querySelectorAll('.slider__icon');

    toggleSidebarElem ?
      toggleSidebarElem.addEventListener('change', function (e) {
        if (e.target.checked) {
          if (tocBodyElem) {
            fadeIn(tocBodyElem, 200);
          }
          if (tocLabelElem) {
            fadeIn(tocLabelElem, 200);
          }
          if (listMainElem && listSideElem) {
            listMainElem.className = 'm';
            listSideElem.className = 'r';
          }

          sliderIcons && sliderIcons.forEach(function (elem) {
            if (elem.classList.contains('hide')) {
              elem.classList.remove('hide');
            } else {
              elem.classList.add('hide');
            }
          });

        } else {
          if (tocBodyElem) {
            fadeOut(tocBodyElem, 200);
          }
          if (tocLabelElem) {
            fadeOut(tocLabelElem, 200);
          }
          if (listMainElem && listSideElem) {
            listMainElem.className = 'mr';
            listSideElem.className = 'hide';
          }

          sliderIcons && sliderIcons.forEach(function (elem) {
            if (elem.classList.contains('hide')) {
              elem.classList.remove('hide');
            } else {
              elem.classList.add('hide');
            }
          });
        }
      }) : null;

    toggleMenuElem ?
      toggleMenuElem.addEventListener('change', function (e) {
        if (e.target.checked) {
          if (listMainElem && singleMenuElem) {
            listMainElem.className = 'm';
            singleMenuElem.className = 'l';
          }

          sliderIcons && sliderIcons.forEach(function (elem) {
            if (elem.classList.contains('hide')) {
              elem.classList.remove('hide');
            } else {
              elem.classList.add('hide');
            }
          });

        } else {
          if (listMainElem && singleMenuElem) {
            listMainElem.className = 'lm';
            singleMenuElem.className = 'hide';
          }

          sliderIcons && sliderIcons.forEach(function (elem) {
            if (elem.classList.contains('hide')) {
              elem.classList.remove('hide');
            } else {
              elem.classList.add('hide');
            }
          });
        }
      }) : null;
    


    
    var navCollapseBtn = document.getElementById('navCollapseBtn');
    navCollapseBtn ? navCollapseBtn.addEventListener('click', function(e) {
      var navCollapse = document.querySelector('.navbar__collapse');
      
      if (navCollapse) {
        var dataOpen = navCollapse.getAttribute('data-open');

        if (dataOpen === 'true') {
          navCollapse.setAttribute('data-open', 'false');
          navCollapse.style.maxHeight = 0;
        } else {
          navCollapse.setAttribute('data-open', 'true');
          navCollapse.style.maxHeight = navCollapse.scrollHeight + "px";
        }
      }
    }) : null;
    


    
    var expandBtn = document.querySelectorAll('.expand__button');

    for (let i = 0; i < expandBtn.length; i++) {
      expandBtn[i].addEventListener("click", function () {
        var content = this.nextElementSibling;
        if (content.style.maxHeight) {
          content.style.maxHeight = null;
          this.querySelector('svg').classList.add('expand-icon__right');
          this.querySelector('svg').classList.remove('expand-icon__down');
        } else {
          content.style.maxHeight = content.scrollHeight + "px";
          this.querySelector('svg').classList.remove('expand-icon__right');
          this.querySelector('svg').classList.add('expand-icon__down');
        }
      });
    }
    



    
    document.querySelectorAll('.tab') ? 
    document.querySelectorAll('.tab').forEach(function(elem, idx) {
      var containerId = elem.getAttribute('id');
      var containerElem = elem;
      var tabLinks = elem.querySelectorAll('.tab__link');
      var tabContents = elem.querySelectorAll('.tab__content');
      var ids = [];

      tabLinks && tabLinks.length > 0 ?
      tabLinks.forEach(function(link, index, self) {
        link.onclick = function(e) {
          for (var i = 0; i < self.length; i++) {
            if (index === parseInt(i, 10)) {
              if (!self[i].classList.contains('active')) {
                self[i].classList.add('active');
                tabContents[i].style.display = 'block';
              }
            } else {
              self[i].classList.remove('active');
              tabContents[i].style.display = 'none';
            }
          }
        }
      }) : null;
    }) : null;
    


    
    document.querySelectorAll('.codetab') ? 
    document.querySelectorAll('.codetab').forEach(function(elem, idx) {
      var containerId = elem.getAttribute('id');
      var containerElem = elem;
      var codetabLinks = elem.querySelectorAll('.codetab__link');
      var codetabContents = elem.querySelectorAll('.codetab__content');
      var ids = [];

      codetabLinks && codetabLinks.length > 0 ?
      codetabLinks.forEach(function(link, index, self) {
        link.onclick = function(e) {
          for (var i = 0; i < self.length; i++) {
            if (index === parseInt(i, 10)) {
              if (!self[i].classList.contains('active')) {
                self[i].classList.add('active');
                codetabContents[i].style.display = 'block';
              }
            } else {
              self[i].classList.remove('active');
              codetabContents[i].style.display = 'none';
            }
          }
        }
      }) : null;
    }) : null;
    

    
    
    
    var enableDarkMode = JSON.parse("true");
    var root = document.getElementById('root');
    var toggleToLightBtn = document.getElementById('toggleToLight');
    var toggleToDarkBtn = document.getElementById('toggleToDark');

    if (!enableDarkMode) {
      root.className = 'theme__light';
      localStorage.setItem('theme', 'light');
    }
    
    if (toggleToDarkBtn) {
      toggleToDarkBtn.onclick = function (e) {
        root.className = 'theme__dark';
        localStorage.setItem('theme', 'dark');
        toggleToLightBtn.className = 'navbar__icons--icon';
        toggleToDarkBtn.className = 'hide';
      }
    }

    if (toggleToLightBtn) {
      toggleToLightBtn.onclick = function (e) {
        root.className = 'theme__light';
        localStorage.setItem('theme', 'light');
        toggleToLightBtn.className = 'hide';
        toggleToDarkBtn.className = 'navbar__icons--icon';
      }
    }

    
    document.querySelectorAll('.menu__list').forEach(function(elem) {
      if (elem.classList.contains('active')) {
        elem.style.maxHeight = elem.scrollHeight + "px"; 
      }
    });
    
    document.querySelectorAll('.menu__title--collapse').forEach(function(elem) {
      elem.addEventListener('click', function (e) {
        var content = this.nextElementSibling;
        var menuTitleIcon = this.querySelector('.menu__title--icon');
        if (!content) {
          return null;
        }

        var parent = elem.parentNode;
        while (parent.classList.contains('menu__list') && parent.classList.contains('active')) {
          parent.style.maxHeight = 100 * parent.children.length + "px";
          parent = parent.parentNode;
        }

        if (content.style.maxHeight) {
          content.style.maxHeight = null;
          content.classList.remove('active');
          menuTitleIcon.classList.add('right');
          
          if (languagedir === 'rtl') {
            menuTitleIcon.classList.remove('downrtl');
          } else {
            menuTitleIcon.classList.remove('down');
          }
        } else {
          content.style.maxHeight = content.scrollHeight + "px";
          content.classList.add('active');
          menuTitleIcon.classList.remove('right');
          
          if (languagedir === 'rtl') {
            menuTitleIcon.classList.add('downrtl');
          } else {
            menuTitleIcon.classList.add('down');
          }
        }
      });
    });
    


    
    var mobileLogo = document.getElementById('mobileLogo');
    var modal = document.getElementById("myModal");
    var drawer = document.getElementById('myDrawer');
    var drawerCloseBtn = document.querySelector('.drawer__close');

    var openDrawer = function() {
      modal.style.opacity = 1;

      if (languagedir === 'rtl') {
        modal.style.right = 0;
        drawer.style.right = 0;
      } else {
        modal.style.left = 0;
        drawer.style.left = 0;
      }
    }

    var closeDrawer = function() {
      modal.style.opacity = 0;

      if (languagedir === 'rtl') {
        drawer.style.right = '-100%';
      } else {
        drawer.style.left = '-100%';
      }

      
      setTimeout(function () {
        if (languagedir === 'rtl') {
          modal.style.right = '-100%';
        } else {
          modal.style.left = '-100%';
        }
      }, 250);
    }

    mobileLogo.onclick = function () {
      openDrawer();
      localStorage.setItem('isDrawerOpen', 'true');
    }

    modal.onclick = function () {
      closeDrawer();
      localStorage.setItem('isDrawerOpen', 'false');
    }

    drawerCloseBtn.onclick = function () {
      closeDrawer();
      localStorage.setItem('isDrawerOpen', 'false');
    }
  


  
    var lastScrollTop = window.pageYOffset || document.documentElement.scrollTop;
    var tocElem = document.querySelector('.toc');
    var tableOfContentsElem = tocElem ? tocElem.querySelector('#TableOfContents') : null;
    var singleContentsElem = document.querySelector('.single__contents');
    var dataBGImgs = document.querySelectorAll('div[data-bgimg]');

    
    var tocLevels = JSON.parse("[\"h2\",\"h3\",\"h4\"]");

    if (tocLevels) {
      tocLevels = tocLevels.toString();
    } else {
      tocLevels = "h1, h2, h3, h4, h5, h6";
    }

    
    var isLandingBgImg = JSON.parse("null");
    
    var isHome = JSON.parse("false");

    function setNavbarBG(scrollTop) {
      if (isHome && isLandingBgImg && Object.keys(isLandingBgImg).length) {
        if (isLandingBgImg.height <= scrollTop) {
          dataBGImgs.forEach(function(elem) {
            elem.setAttribute('data-bgimg', 'false');
          });
        } else {
          dataBGImgs.forEach(function (elem) {
            elem.setAttribute('data-bgimg', 'true');
          });
        }
      }
    }
    setNavbarBG(lastScrollTop);

    window.onscroll = function () {
      var st = window.pageYOffset || document.documentElement.scrollTop;
      if (st > lastScrollTop) { 
        singleContentsElem ? 
        singleContentsElem.querySelectorAll(tocLevels.toString()).forEach(function(elem) {
          if (document.documentElement.scrollTop >= elem.offsetTop) {
            if (tableOfContentsElem) {
              var id = elem.getAttribute('id');
              tocElem.querySelectorAll('a').forEach(function (elem) {
                elem.classList.remove('active');
              });
              tocElem.querySelector('a[href="#' + id + '"]') ? 
              tocElem.querySelector('a[href="#' + id + '"]').classList.add('active') : null;
            }
          }
        }) : null;
        setNavbarBG(st);
      } else { 
        singleContentsElem ? 
        singleContentsElem.querySelectorAll(tocLevels.toString()).forEach(function(elem) {
          if (document.documentElement.scrollTop >= elem.offsetTop) {
            if (tableOfContentsElem) {
              var id = elem.getAttribute('id');
              tocElem.querySelectorAll('a').forEach(function (elem) {
                elem.classList.remove('active');
              });
              tocElem.querySelector('a[href="#' + id + '"]') ? 
              tocElem.querySelector('a[href="#' + id + '"]').classList.add('active') : null;
            }
          }
        }) : null;
        setNavbarBG(st);
      }
      lastScrollTop = st <= 0 ? 0 : st;
    };
    



    
    var mobileSearchInputElem = document.querySelector('#search-mobile');
    var mobileSearchClassElem = document.querySelector('.mobile-search');
    var mobileSearchBtnElem = document.querySelector('#mobileSearchBtn');
    var mobileSearchCloseBtnElem = document.querySelector('#search-mobile-close');
    var mobileSearchContainer = document.querySelector('#search-mobile-container');
    var mobileSearchResultsElem = document.querySelector('#search-mobile-results');
    var htmlElem = document.querySelector('html');

    if (mobileSearchClassElem) {
      mobileSearchClassElem.style.display = 'none';
    }

    mobileSearchBtnElem ? 
    mobileSearchBtnElem.addEventListener('click', function () {
      if (mobileSearchContainer) {
        mobileSearchContainer.style.display = 'block';
      }

      if (mobileSearchInputElem) {
        mobileSearchInputElem.focus();
      }

      if (htmlElem) {
        htmlElem.style.overflowY = 'hidden';
      }
    }) : null;

    mobileSearchCloseBtnElem ? 
    mobileSearchCloseBtnElem.addEventListener('click', function() {
      if (mobileSearchContainer) {
        mobileSearchContainer.style.display = 'none';
      }

      if (mobileSearchInputElem) {
        mobileSearchInputElem.value = '';
      }
      
      if (mobileSearchResultsElem) {
        while (mobileSearchResultsElem.firstChild) {
          mobileSearchResultsElem.removeChild(mobileSearchResultsElem.firstChild);
        }
      }

      if (htmlElem) {
        htmlElem.style.overflowY = 'visible';
      }
    }) : null;

    mobileSearchInputElem ? 
    mobileSearchInputElem.addEventListener('keydown', function(e) {
      if (e.key === 'Escape') {
        if (mobileSearchContainer) {
          mobileSearchContainer.style.display = 'none';
        }
        
        if (mobileSearchInputElem) {
          mobileSearchInputElem.value = '';
        }

        if (mobileSearchResultsElem) {
          while (mobileSearchResultsElem.firstChild) {
            mobileSearchResultsElem.removeChild(mobileSearchResultsElem.firstChild);
          }
        }
        if (htmlElem) {
          htmlElem.style.overflowY = 'visible';
        }
      }
    }) : null;
  


  
    var localTheme = localStorage.getItem('theme');
    var rootEleme = document.getElementById('root');
    var selectThemeElem = document.querySelectorAll('.select-theme');
    var selectThemeItemElem = document.querySelectorAll('.select-theme__item');
    
    if (localTheme) {
      selectThemeItemElem ? 
      selectThemeItemElem.forEach(function (elem) {
        if (elem.text.trim() === localTheme) {
          elem.classList.add('is-active');
        } else {
          elem.classList.remove('is-active');
        }
      }) : null;
    }

    selectThemeItemElem ? 
    selectThemeItemElem.forEach(function (v, i) {
      v.addEventListener('click', function (e) {
        var selectedThemeVariant = e.target.text.trim();
        localStorage.setItem('theme', selectedThemeVariant);

        rootEleme.removeAttribute('class');
        rootEleme.classList.add(`theme__${selectedThemeVariant}`);
        selectThemeElem.forEach(function(rootElem) {
          rootElem.querySelectorAll('a').forEach(function (elem) {
            if (elem.classList) {
              if (elem.text.trim() === selectedThemeVariant) {
                if (!elem.classList.contains('is-active')) {
                  elem.classList.add('is-active');
                }
              } else {
                if (elem.classList.contains('is-active')) {
                  elem.classList.remove('is-active');
                }
              }
            }
          });
        });

        if (window.mermaid) {
          if (selectedThemeVariant === "dark" || selectedThemeVariant === "hacker") {
            mermaid.initialize({ theme: 'dark' });
            location.reload();
          } else {
            mermaid.initialize({ theme: 'default' });
            location.reload();
          }
        }

        var utterances = document.querySelector('iframe');
        if (utterances) {
          utterances.contentWindow.postMessage({
            type: 'set-theme',
            theme: selectedThemeVariant === "dark" || selectedThemeVariant === "hacker" ? 'photon-dark' : selectedThemeVariant === 'kimbie' ? 'github-dark-orange' : 'github-light',
          }, 'https://utteranc.es');
        }
      });
    }) : null;
  


  
    
    var langprefix = JSON.parse("\"\"");
    var searchResults = null;
    var searchMenu = null;
    var searchText = null;
    
    
    var enableSearchHighlight = JSON.parse("true");
    
    var enableSearch = JSON.parse("true");

    var fuse = null;

    if (enableSearch) {
      (function initFuse() {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', baseurl + langprefix + "/index.json");
        xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
        xhr.onload = function () {
          if (xhr.status === 200) {
            fuse = new Fuse(JSON.parse(xhr.response.toString('utf-8')), {
              keys: ['title', 'description', 'content'],
              includeMatches: enableSearchHighlight,
              shouldSort: true,
              threshold: 0.4,
              location: 0,
              distance: 100,
              maxPatternLength: 32,
              minMatchCharLength: 1,
            });
          }
          else {
            console.error(`[${xhr.status}]Error:`, xhr.statusText);
          }
        };
        xhr.send();
      })();
    }
    
    function makeLi(ulElem, obj) {
      var li = document.createElement('li');
      li.className = 'search-result__item';
      
      var a = document.createElement('a');
      a.innerHTML = obj.title;
      a.setAttribute('class', 'search-result__item--title');
      a.setAttribute('href', obj.permalink);

      var descDiv = document.createElement('div');
      descDiv.setAttribute('class', 'search-result__item--desc');
      if (obj.description) {
        descDiv.innerHTML = obj.description;
      } else if (obj.content) {
        descDiv.innerHTML = obj.content.substring(0, 80);
      }
      
      li.appendChild(a);
      li.appendChild(descDiv);
      ulElem.appendChild(li);
    }

    function makeHighlightLi(ulElem, obj) {
      var li = document.createElement('li');
      li.className = 'search-result__item';
      var descDiv = null;

      var a = document.createElement('a');
      a.innerHTML = obj.item.title;
      a.setAttribute('class', 'search-result__item--title');
      a.setAttribute('href', obj.item.uri);

      if (obj.matches && obj.matches.length) {
        for (var i = 0; i < obj.matches.length; i++) {
          if ('title' === obj.matches[i].key) {
            a = document.createElement('a');
            a.innerHTML = generateHighlightedText(obj.matches[i].value, obj.matches[i].indices);
            a.setAttribute('class', 'search-result__item--title');
            a.setAttribute('href', obj.item.uri);
          }
          
          if ('description' === obj.matches[i].key) {
            descDiv = document.createElement('div');
            descDiv.setAttribute('class', 'search-result__item--desc');
            descDiv.innerHTML = generateHighlightedText(obj.item.description, obj.matches[i].indices);
          } else if ('content' === obj.matches[i].key) {
            if (!descDiv) {
              descDiv = document.createElement('div');
              descDiv.setAttribute('class', 'search-result__item--desc');
              descDiv.innerHTML = generateHighlightedText(obj.item.content.substring(0, 80), obj.matches[i].indices);
            }
          } else {
            if (obj.item.description) {
              descDiv = document.createElement('div');
              descDiv.setAttribute('class', 'search-result__item--desc');
              descDiv.innerHTML = obj.item.description;
            } else {
              descDiv = document.createElement('div');
              descDiv.setAttribute('class', 'search-result__item--desc');
              descDiv.innerHTML = obj.item.content.substring(0, 80);
            }
          }
        }

        li.appendChild(a);
        if (descDiv) {
          li.appendChild(descDiv);
        }
        if (li) {
          ulElem.appendChild(li);
        }
      }
    }

    function renderSearchResults(searchText, results) {
      searchResults = document.getElementById('search-results');
      searchMenu = document.getElementById('search-menu');
      searchResults.setAttribute('class', 'dd is-active');
      
      var ul = document.createElement('ul');
      ul.setAttribute('class', 'dd-content search-content');

      if (results.length) {
        results.forEach(function (result) {
          var li = document.createElement('li');
          var a = document.createElement('a');
          a.setAttribute('href', result.uri);
          a.setAttribute('class', 'dd-item');
          a.appendChild(li);

          var titleDiv = document.createElement('div');
          titleDiv.innerHTML = result.title;
          titleDiv.setAttribute('class', 'search-result__item--title');

          var descDiv = document.createElement('div');
          descDiv.setAttribute('class', 'search-result__item--desc');
          if (result.description) {
            descDiv.innerHTML = result.description;
          } else if (result.content) {
            descDiv.innerHTML = result.content.substring(0, 80);
          }

          li.appendChild(titleDiv);
          li.appendChild(descDiv);
          ul.appendChild(a);
        });
      } else {
        var li = document.createElement('li');
        li.setAttribute('class', 'dd-item');
        li.innerText = 'No results found';
        ul.appendChild(li);
      }

      while (searchMenu.hasChildNodes()) {
        searchMenu.removeChild(
          searchMenu.lastChild
        );
      }
      
      searchMenu.appendChild(ul);
    }

    function renderSearchHighlightResults(searchText, results) {
      searchResults = document.getElementById('search-results');
      searchMenu = document.getElementById('search-menu');
      searchResults.setAttribute('class', 'dd is-active');

      var ul = document.createElement('ul');
      ul.setAttribute('class', 'dd-content search-content');

      if (results.length) {
        results.forEach(function (result) {
          var li = document.createElement('li');
          var a = document.createElement('a');
          var descDiv = null;

          a.setAttribute('href', result.item.uri);
          a.setAttribute('class', 'dd-item');
          a.appendChild(li);

          var titleDiv = document.createElement('div');
          titleDiv.innerHTML = result.item.title;
          titleDiv.setAttribute('class', 'search-result__item--title');
          
          if (result.matches && result.matches.length) {
            for (var i = 0; i < result.matches.length; i++) {
              if ('title' === result.matches[i].key) {
                titleDiv.innerHTML = generateHighlightedText(result.matches[i].value, result.matches[i].indices);
              }

              if ('description' === result.matches[i].key) {
                descDiv = document.createElement('div');
                descDiv.setAttribute('class', 'search-result__item--desc');
                descDiv.innerHTML = generateHighlightedText(result.item.description, result.matches[i].indices);
              } else if ('content' === result.matches[i].key) {
                if (!descDiv) {
                  descDiv = document.createElement('div');
                  descDiv.setAttribute('class', 'search-result__item--desc');
                  descDiv.innerHTML = generateHighlightedText(result.item.content.substring(0, 80), result.matches[i].indices);
                }
              } else {
                if (result.item.description) {
                  descDiv = document.createElement('div');
                  descDiv.setAttribute('class', 'search-result__item--desc');
                  descDiv.innerHTML = result.item.description;
                } else {
                  descDiv = document.createElement('div');
                  descDiv.setAttribute('class', 'search-result__item--desc');
                  descDiv.innerHTML = result.item.content.substring(0, 80);
                }
              }
            }
            
            li.appendChild(titleDiv);
            if (descDiv) {
              li.appendChild(descDiv);
            }
            ul.appendChild(a);
          }
        });
      } else {
        var li = document.createElement('li');
        li.setAttribute('class', 'dd-item');
        li.innerText = 'No results found';
        ul.appendChild(li);
      }

      while (searchMenu.hasChildNodes()) {
        searchMenu.removeChild(
          searchMenu.lastChild
        );
      }
      searchMenu.appendChild(ul);
    }

    function renderSearchResultsMobile(searchText, results) {
      searchResults = document.getElementById('search-mobile-results');

      var content = document.createElement('div');
      content.setAttribute('class', 'mobile-search__content');

      if (results.length > 0) {
        results.forEach(function (result) {
          var item = document.createElement('a');
          item.setAttribute('href', result.uri);
          item.innerHTML = '<div class="mobile-search__item"><div class="mobile-search__item--title">📄 ' + result.title + '</div><div class="mobile-search__item--desc">' + (result.description ? result.description : result.content) + '</div></div>';
          content.appendChild(item);
        });
      } else {
        var item = document.createElement('span');
        content.appendChild(item);
      }

      let wrap = document.getElementById('search-mobile-results');
      while (wrap.firstChild) {
        wrap.removeChild(wrap.firstChild)
      }
      searchResults.appendChild(content);      
    }

    function renderSearchHighlightResultsMobile(searchText, results) {
      searchResults = document.getElementById('search-mobile-results');

      var ul = document.createElement('div');
      ul.setAttribute('class', 'mobile-search__content');

      if (results.length) {
        results.forEach(function (result) {
          var li = document.createElement('li');
          var a = document.createElement('a');
          var descDiv = null;

          a.setAttribute('href', result.item.uri);
          a.appendChild(li);
          li.setAttribute('class', 'mobile-search__item');

          var titleDiv = document.createElement('div');
          titleDiv.innerHTML = result.item.title;
          titleDiv.setAttribute('class', 'mobile-search__item--title');
          
          if (result.matches && result.matches.length) {
            for (var i = 0; i < result.matches.length; i++) {
              if ('title' === result.matches[i].key) {
                titleDiv.innerHTML = generateHighlightedText(result.matches[i].value, result.matches[i].indices);
              }

              if ('description' === result.matches[i].key) {
                descDiv = document.createElement('div');
                descDiv.setAttribute('class', 'mobile-search__item--desc');
                descDiv.innerHTML = generateHighlightedText(result.item.description, result.matches[i].indices);
              } else if ('content' === result.matches[i].key) {
                if (!descDiv) {
                  descDiv = document.createElement('div');
                  descDiv.setAttribute('class', 'mobile-search__item--desc');
                  descDiv.innerHTML = generateHighlightedText(result.item.content.substring(0, 150), result.matches[i].indices);
                }
              } else {
                if (result.item.description) {
                  descDiv = document.createElement('div');
                  descDiv.setAttribute('class', 'mobile-search__item--desc');
                  descDiv.innerHTML = result.item.description;
                } else {
                  descDiv = document.createElement('div');
                  descDiv.setAttribute('class', 'mobile-search__item--desc');
                  descDiv.innerHTML = result.item.content.substring(0, 150);
                }
              }
            }
            
            li.appendChild(titleDiv);
            if (descDiv) {
              li.appendChild(descDiv);
            }
            ul.appendChild(a);
          }
        });
      } else {
        var item = document.createElement('span');
        ul.appendChild(item);
      }

      let wrap = document.getElementById('search-mobile-results');
      while (wrap.firstChild) {
        wrap.removeChild(wrap.firstChild)
      }
      searchResults.appendChild(ul);
    }

    function generateHighlightedText(text, regions) {
      if (!regions) {
        return text;
      }

      var content = '', nextUnhighlightedRegionStartingIndex = 0;

      regions.forEach(function(region) {
        if (region[0] === region[1]) {
          return null;
        }
        
        content += '' +
          text.substring(nextUnhighlightedRegionStartingIndex, region[0]) +
          '<span class="search__highlight">' +
            text.substring(region[0], region[1] + 1) +
          '</span>' +
        '';
        nextUnhighlightedRegionStartingIndex = region[1] + 1;
      });

      content += text.substring(nextUnhighlightedRegionStartingIndex);

      return content;
    };

    var searchElem = document.getElementById('search');
    var searchMobile = document.getElementById('search-mobile');
    var searchResultsContainer = document.getElementById('search-results');

    searchElem ? 
    searchElem.addEventListener('input', function(e) {
      if (!e.target.value | window.innerWidth < 770) {
        searchResultsContainer ? searchResultsContainer.setAttribute('class', 'dd') : null;
        return null;
      }

      searchText = e.target.value;
      var results = fuse.search(e.target.value);

      if (enableSearchHighlight) {
        renderSearchHighlightResults(searchText, results);
      } else {
        renderSearchResults(searchText, results);
      }

      var dropdownItems = searchResultsContainer.querySelectorAll('.dd-item');
      dropdownItems ? dropdownItems.forEach(function (item) {
        item.addEventListener('mousedown', function (e) {
          e.target.click();
        });
      }) : null;
    }) : null;

    searchElem ? 
    searchElem.addEventListener('blur', function() {
      if (window.innerWidth < 770) {
        return null;
      }
      searchResultsContainer ? searchResultsContainer.setAttribute('class', 'dd') : null;
    }) : null;

    searchElem ? 
    searchElem.addEventListener('click', function(e) {
      if (window.innerWidth < 770) {
        return null;
      }
      if (!e.target.value) {
        searchResultsContainer ? searchResultsContainer.setAttribute('class', 'dd') : null;
        return null;
      }

      searchText = e.target.value;
      var results = fuse.search(e.target.value);

      if (enableSearchHighlight) {
        renderSearchHighlightResults(searchText, results);
      } else {
        renderSearchResults(searchText, results);
      }

      var dropdownItems = searchResultsContainer.querySelectorAll('.dd-item');
      dropdownItems ? dropdownItems.forEach(function (item) {
        item.addEventListener('mousedown', function (e) {
          e.target.click();
        });
      }) : null;
    }) : null;

    var searchMenuElem = document.getElementById("search-menu");
    var activeItem = document.querySelector('#search-menu .dd-item.is-active');
    var activeIndex = null;
    var items = null;
    var searchContainerMaxHeight = 350;

    searchElem ? 
    searchElem.addEventListener('keydown', function(e) {
      if (window.innerWidth < 770) {
        return null;
      }

      var items = document.querySelectorAll('#search-menu .dd-item');
      
      if (e.key === 'ArrowDown') {
        if (activeIndex === null) {
          activeIndex = 0;
          items[activeIndex].classList.remove('is-active');
        } else {
          items[activeIndex].classList.remove('is-active');
          activeIndex = activeIndex === items.length - 1 ? 0 : activeIndex + 1;
        }
        items[activeIndex].classList.add('is-active');

        let overflowedPixel = items[activeIndex].offsetTop + items[activeIndex].clientHeight - searchContainerMaxHeight;
        if (overflowedPixel > 0) {
          document.querySelector(".search-content").scrollTop += items[activeIndex].getBoundingClientRect().height;
        } else if (activeIndex === 0) {
          document.querySelector(".search-content").scrollTop = 0;
        }
      } else if (e.key === 'ArrowUp') {
        if (activeIndex === null) {
          activeIndex = items.length - 1;
          items[activeIndex].classList.remove('is-active');
        } else {
          items[activeIndex].classList.remove('is-active');
          activeIndex = activeIndex === 0 ? items.length - 1 : activeIndex - 1;
        }
        items[activeIndex].classList.add('is-active');
        
        let overflowedPixel = items[activeIndex].offsetTop + items[activeIndex].clientHeight - searchContainerMaxHeight;
        if (overflowedPixel < 0) {
          document.querySelector(".search-content").scrollTop -= items[activeIndex].getBoundingClientRect().height;
        } else {
          document.querySelector(".search-content").scrollTop = overflowedPixel + items[activeIndex].getBoundingClientRect().height;
        }
      } else if (e.key === 'Enter') {
        var currentItemLink = items[activeIndex].getAttribute('href');
        if (currentItemLink) {
          location.href = currentItemLink;
        }
      } else if (e.key === 'Escape') {
        e.target.value = null;
        if (searchResults) {
          searchResults.classList.remove('is-active');
        }
      }
    }) : null;

    searchMobile ? 
    searchMobile.addEventListener('input', function(e) {
      if (!e.target.value) {
        let wrap = document.getElementById('search-mobile-results');
        while (wrap.firstChild) {
          wrap.removeChild(wrap.firstChild);
        }
        return null;
      }

      searchText = e.target.value;
      var results = fuse.search(e.target.value);
      
      if (enableSearchHighlight) {
        renderSearchHighlightResultsMobile(searchText, results);
      } else {
        renderSearchResultsMobile(searchText, results);
      }

      var dropdownItems = searchResultsContainer.querySelectorAll('.dd-item');
      dropdownItems ? dropdownItems.forEach(function (item) {
        item.addEventListener('mousedown', function (e) {
          e.target.click();
        });
      }) : null;
    }) : null;
  
  }
</script>
    


<link rel="stylesheet" href="/css/main.min.css">


    <meta name="description" content="Hook 是 React 16.8 的新增特性。它可以在不编写 class 的情况下使用 state 以及其他的 React 特性" />
<meta name="keywords" content="">
<meta name="created" content="2020-01-28T00:34:39&#43;0900">
<meta name="modified" content="2020-01-28T00:34:39&#43;0900">


<meta property="og:site_name" content="codthing">
<meta property="og:title" content="Hooks 最佳实践">
<meta property="og:url" content="https://codthing.github.io/react/hooks/hooks-bp/">
<meta property="og:type" content="article">

<meta name="generator" content="Hugo 0.95.0" />
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="theme-color" content="#ffffff">
<link rel="canonical" href="https://codthing.github.io/react/hooks/hooks-bp/">
<link rel="manifest" href="/manifest.json">

  <link rel="apple-touch-icon" sizes="57x57" href="/favicon/apple-icon-57x57.png">
  <link rel="apple-touch-icon" sizes="60x60" href="/favicon/apple-icon-60x60.png">
  <link rel="apple-touch-icon" sizes="72x72" href="/favicon/apple-icon-72x72.png">
  <link rel="apple-touch-icon" sizes="76x76" href="/favicon/apple-icon-76x76.png">
  <link rel="apple-touch-icon" sizes="114x114" href="/favicon/apple-icon-114x114.png">
  <link rel="apple-touch-icon" sizes="120x120" href="/favicon/apple-icon-120x120.png">
  <link rel="apple-touch-icon" sizes="144x144" href="/favicon/apple-icon-144x144.png">
  <link rel="apple-touch-icon" sizes="152x152" href="/favicon/apple-icon-152x152.png">
  <link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-icon-180x180.png">
  <link rel="icon" type="image/png" sizes="192x192" href="/favicon/android-icon-192x192.png">
  <link rel="icon" type="image/png" sizes="192x192" href="/favicon/android-icon-512x512.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png">
  <link rel="icon" type="image/png" sizes="96x96" href="/favicon/favicon-96x96.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png">
  <meta name="msapplication-TileColor" content="#ffffff">
  <meta name="msapplication-TileImage" content="/ms-icon-144x144.png">

    <script type="application/ld+json">
  {
    "@context": "https://schema.org",
    "@type": "WebPage",
    "headline": "Hooks 最佳实践",
    "datePublished": "2020-01-28T00:34:39+09:00",
    "dateModified": "2020-01-28T00:34:39+09:00",
    "url" : "https://codthing.github.io/react/hooks/hooks-bp/",
    "description": "Hook 是 React 16.8 的新增特性。它可以在不编写 class 的情况下使用 state 以及其他的 React 特性",
    "mainEntityOfPage": {
      "@type": "WebPage",
      "@id": "https://codthing.github.io/"
    },
    "publisher": {
      "@type": "Organization",
      "name": "codthing",
      "url": "https://codthing.github.io/"
    }
  }
</script>

    

</head>


<body id="root" class="theme__dark">
    <script>
        var rootElem = document.getElementById('root');
        var themeOptions = JSON.parse("[\"dark\",\"light\"]");
        var localTheme = localStorage.getItem('theme');

        if (localTheme) {
            if (themeOptions && themeOptions.length > 0) {
                themeOptions.includes(localTheme) ? 
                rootElem.className = `theme__${localTheme}` :
                rootElem.className = `theme__${themeOptions[0]}`;
            }
        }
    </script>
    
    <div id="container">
        <div id="myDrawer" class="drawer" style="">
  <div class="drawer__header">
    <a href="/" class="drawer__header--text">
      
    </a>
    <div class="grow"></div>
    <div class="drawer__close">
      <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path fill="currentColor" d="M18.3 5.71c-.39-.39-1.02-.39-1.41 0L12 10.59 7.11 5.7c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.02 0 1.41L10.59 12 5.7 16.89c-.39.39-.39 1.02 0 1.41.39.39 1.02.39 1.41 0L12 13.41l4.89 4.89c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L13.41 12l4.89-4.89c.38-.38.38-1.02 0-1.4z"/></svg>
    </div>
  </div>

  <div class="drawer__body">
    <div class="menu">
  <h6 class="menu__label">
    
      Hooks 最佳实践
    
  </h6>
  <ul>
    
    
      
  
  
  
  
  
  
  

  
  
  
  
  
  
  
  <li class="">
    <a href="https://codthing.github.io/react/redux/react-redux/"
      class="menu__title ">React &amp; Redux</a>
  </li>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  
  
  <li class="">
    <a href="https://codthing.github.io/react/react/react-base/"
      class="menu__title ">React 基础</a>
  </li>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  
  
  <li class="">
    <a href="https://codthing.github.io/react/react/react-bp/"
      class="menu__title ">React 最佳实践</a>
  </li>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  
  
  <li class="">
    <a href="https://codthing.github.io/react/redux/redux-base/"
      class="menu__title ">Redux</a>
  </li>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  
  
  <li class="active">
    <a href="https://codthing.github.io/react/hooks/hooks-bp/"
      class="menu__title active">Hooks 最佳实践</a>
  </li>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  
  
  <li class="">
    <a href="https://codthing.github.io/react/redux/hooks-redux/"
      class="menu__title ">Hooks &amp; Redux</a>
  </li>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  
  
  <li class="">
    <a href="https://codthing.github.io/react/zustand/zustand-base/"
      class="menu__title ">zustand</a>
  </li>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  
  
  <li class="">
    <a href="https://codthing.github.io/react/recoil/recoil-bp/"
      class="menu__title ">Recoil 最佳实践</a>
  </li>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  <li class="menu__title--collapse " data-depth="0" data-permalink="https://codthing.github.io/react/next/">
    <span class="menu__title--collapse-text">
      Next.js
    </span>
    <span class="menu__title--icon right">
      
        <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path fill="currentColor" d="M9.29 15.88L13.17 12 9.29 8.12c-.39-.39-.39-1.02 0-1.41.39-.39 1.02-.39 1.41 0l4.59 4.59c.39.39.39 1.02 0 1.41L10.7 17.3c-.39.39-1.02.39-1.41 0-.38-.39-.39-1.03 0-1.42z"/></svg>
      
    </span>
  </li>
  <ul class="menu__list " data-data=/next/ data-link=https://codthing.github.io/react/hooks/hooks-bp/>
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/next/next-base/" class="menu__title " data-depth="0">Next.js 速览</a>
        </li>
      
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/next/next-content/" class="menu__title " data-depth="0">Next.js 核心工作内容</a>
        </li>
      
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/next/next-multiple/" class="menu__title " data-depth="0">Next.js 各种组合</a>
        </li>
      
    
  </ul>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  
  
  <li class="">
    <a href="https://codthing.github.io/react/remix/remix-base/"
      class="menu__title ">Remix 基础</a>
  </li>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  
  
  <li class="">
    <a href="https://codthing.github.io/react/remix/remix-adv/"
      class="menu__title ">Remix 进阶</a>
  </li>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  <li class="menu__title--collapse " data-depth="0" data-permalink="https://codthing.github.io/react/storybook/">
    <span class="menu__title--collapse-text">
      Storybook
    </span>
    <span class="menu__title--icon right">
      
        <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path fill="currentColor" d="M9.29 15.88L13.17 12 9.29 8.12c-.39-.39-.39-1.02 0-1.41.39-.39 1.02-.39 1.41 0l4.59 4.59c.39.39.39 1.02 0 1.41L10.7 17.3c-.39.39-1.02.39-1.41 0-.38-.39-.39-1.03 0-1.42z"/></svg>
      
    </span>
  </li>
  <ul class="menu__list " data-data=/storybook/ data-link=https://codthing.github.io/react/hooks/hooks-bp/>
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/storybook/sb-bp/" class="menu__title " data-depth="0">Storybook 最佳实践</a>
        </li>
      
    
      
        
  
  
  
  
  
  
  

  
  
  
  
  
  <li class="menu__title--collapse " data-depth="1" data-permalink="https://codthing.github.io/react/storybook/sb-started/">
    <span class="menu__title--collapse-text">
      开始
    </span>
    <span class="menu__title--icon right">
      
        <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path fill="currentColor" d="M9.29 15.88L13.17 12 9.29 8.12c-.39-.39-.39-1.02 0-1.41.39-.39 1.02-.39 1.41 0l4.59 4.59c.39.39.39 1.02 0 1.41L10.7 17.3c-.39.39-1.02.39-1.41 0-.38-.39-.39-1.03 0-1.42z"/></svg>
      
    </span>
  </li>
  <ul class="menu__list " data-data=/sb-started/ data-link=https://codthing.github.io/react/hooks/hooks-bp/>
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/storybook/sb-started/sb-introduction/" class="menu__title " data-depth="1">介绍</a>
        </li>
      
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/storybook/sb-started/sb-install/" class="menu__title " data-depth="1">安装</a>
        </li>
      
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/storybook/sb-started/sb-what/" class="menu__title " data-depth="1">Story 是什么？</a>
        </li>
      
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/storybook/sb-started/sb-browse/" class="menu__title " data-depth="1">浏览故事</a>
        </li>
      
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/storybook/sb-started/sb-setup/" class="menu__title " data-depth="1">设置</a>
        </li>
      
    
  </ul>
  

      
    
      
        
  
  
  
  
  
  
  

  
  
  
  
  
  <li class="menu__title--collapse " data-depth="1" data-permalink="https://codthing.github.io/react/storybook/sb-write/">
    <span class="menu__title--collapse-text">
      写故事
    </span>
    <span class="menu__title--icon right">
      
        <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path fill="currentColor" d="M9.29 15.88L13.17 12 9.29 8.12c-.39-.39-.39-1.02 0-1.41.39-.39 1.02-.39 1.41 0l4.59 4.59c.39.39.39 1.02 0 1.41L10.7 17.3c-.39.39-1.02.39-1.41 0-.38-.39-.39-1.03 0-1.42z"/></svg>
      
    </span>
  </li>
  <ul class="menu__list " data-data=/sb-write/ data-link=https://codthing.github.io/react/hooks/hooks-bp/>
    
  </ul>
  

      
    
      
        
  
  
  
  
  
  
  

  
  
  
  
  
  <li class="menu__title--collapse " data-depth="1" data-permalink="https://codthing.github.io/react/storybook/sb-test/">
    <span class="menu__title--collapse-text">
      编写测试
    </span>
    <span class="menu__title--icon right">
      
        <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path fill="currentColor" d="M9.29 15.88L13.17 12 9.29 8.12c-.39-.39-.39-1.02 0-1.41.39-.39 1.02-.39 1.41 0l4.59 4.59c.39.39.39 1.02 0 1.41L10.7 17.3c-.39.39-1.02.39-1.41 0-.38-.39-.39-1.03 0-1.42z"/></svg>
      
    </span>
  </li>
  <ul class="menu__list " data-data=/sb-test/ data-link=https://codthing.github.io/react/hooks/hooks-bp/>
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/storybook/sb-test/sb-test-introduction/" class="menu__title " data-depth="1">介绍</a>
        </li>
      
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/storybook/sb-test/sb-visual-tests/" class="menu__title " data-depth="1">视觉测试</a>
        </li>
      
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/storybook/sb-test/sb-accessibility-testing/" class="menu__title " data-depth="1">可访问性测试</a>
        </li>
      
    
  </ul>
  

      
    
  </ul>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  <li class="menu__title--collapse " data-depth="0" data-permalink="https://codthing.github.io/react/tailwindcss/">
    <span class="menu__title--collapse-text">
      tailwindcss
    </span>
    <span class="menu__title--icon right">
      
        <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path fill="currentColor" d="M9.29 15.88L13.17 12 9.29 8.12c-.39-.39-.39-1.02 0-1.41.39-.39 1.02-.39 1.41 0l4.59 4.59c.39.39.39 1.02 0 1.41L10.7 17.3c-.39.39-1.02.39-1.41 0-.38-.39-.39-1.03 0-1.42z"/></svg>
      
    </span>
  </li>
  <ul class="menu__list " data-data=/tailwindcss/ data-link=https://codthing.github.io/react/hooks/hooks-bp/>
    
      
        
  
  
  
  
  
  
  

  
  
  
  
  
  <li class="menu__title--collapse " data-depth="1" data-permalink="https://codthing.github.io/react/tailwindcss/tailwindcss-start.md/">
    <span class="menu__title--collapse-text">
      开始
    </span>
    <span class="menu__title--icon right">
      
        <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path fill="currentColor" d="M9.29 15.88L13.17 12 9.29 8.12c-.39-.39-.39-1.02 0-1.41.39-.39 1.02-.39 1.41 0l4.59 4.59c.39.39.39 1.02 0 1.41L10.7 17.3c-.39.39-1.02.39-1.41 0-.38-.39-.39-1.03 0-1.42z"/></svg>
      
    </span>
  </li>
  <ul class="menu__list " data-data=/tailwindcss-start.md/ data-link=https://codthing.github.io/react/hooks/hooks-bp/>
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/tailwindcss/tailwindcss-start.md/tailwindcss-install/" class="menu__title " data-depth="1">安装</a>
        </li>
      
    
  </ul>
  

      
    
  </ul>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  <li class="menu__title--collapse " data-depth="0" data-permalink="https://codthing.github.io/react/wechat-mini/">
    <span class="menu__title--collapse-text">
      小程序开发
    </span>
    <span class="menu__title--icon right">
      
        <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path fill="currentColor" d="M9.29 15.88L13.17 12 9.29 8.12c-.39-.39-.39-1.02 0-1.41.39-.39 1.02-.39 1.41 0l4.59 4.59c.39.39.39 1.02 0 1.41L10.7 17.3c-.39.39-1.02.39-1.41 0-.38-.39-.39-1.03 0-1.42z"/></svg>
      
    </span>
  </li>
  <ul class="menu__list " data-data=/wechat-mini/ data-link=https://codthing.github.io/react/hooks/hooks-bp/>
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/wechat-mini/taro-log/" class="menu__title " data-depth="0">Taro开发日志</a>
        </li>
      
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/wechat-mini/remax-log/" class="menu__title " data-depth="0">Remax开发日志</a>
        </li>
      
    
  </ul>
  

    
  </ul>
</div>



<script>
  var menuTitle = document.querySelectorAll('.menu__title');
  var modal = document.getElementById("myModal");
  var drawer = document.getElementById('myDrawer');

  var closeDrawer = function () {
    setTimeout(function () {
      modal.style.opacity = 0;
      drawer.style.left = '-100%';
      modal.style.left = '-100%';
    }, 250);
  }

  menuTitle ? 
  menuTitle.forEach(function(elem) {
    elem.onclick = function() {
      closeDrawer();
      localStorage.setItem('isDrawerOpen', 'false');
    }
  }) : null;
</script>
  </div>
</div>
<div id="myModal" class="modal" style=""></div>

<script>
  var isDrawerOpen = localStorage.getItem('isDrawerOpen');
  var modal = document.getElementById("myModal");
  var drawer = document.getElementById('myDrawer');
  
  
  var languagedir = JSON.parse("\"ltr\"");

  var openDrawer = function () {
    modal.style.opacity = 1;
    
    if (languagedir === "rtl") {
      modal.style.right = 0;
      drawer.style.right = 0;
    } else {
      modal.style.left = 0;
      drawer.style.left = 0;
    }
  }

  if (isDrawerOpen && isDrawerOpen === 'true') {
    openDrawer();
  }
</script>
        
<nav class="nav">
  <div class="navbar__wrapper" data-bgimg="false">
    <div class="divider">
      <div class="navbar lmr">
        <div id="siteLogo" class="navbar__logo--wrapper">
  
    &nbsp;&nbsp;
  
  
    <a href="/" title="" rel="home" class="navbar__logo--text-link">
      <h6 class="navbar__logo--text" data-bgimg="false"></h6>
    </a>
  
</div>
        
        <div class="navbar__menu--wrapper">
          <div id="tabletLogo" class="hide">
  
    <a href="/" rel="home" class="navbar__logo--link">
      <div class="navbar__icons--icon">
        <svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path fill="currentColor" d="M10 19v-5h4v5c0 .55.45 1 1 1h3c.55 0 1-.45 1-1v-7h1.7c.46 0 .68-.57.33-.87L12.67 3.6c-.38-.34-.96-.34-1.34 0l-8.36 7.53c-.34.3-.13.87.33.87H5v7c0 .55.45 1 1 1h3c.55 0 1-.45 1-1z"/></svg>
      </div>
    </a>
  
</div>
          <div id="mobileLogo" class="hide">
  <a href="#" class="navbar__logo--link">
    <div class="navbar__icons--icon">
      <svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path fill="currentColor" d="M4 18h16c.55 0 1-.45 1-1s-.45-1-1-1H4c-.55 0-1 .45-1 1s.45 1 1 1zm0-5h16c.55 0 1-.45 1-1s-.45-1-1-1H4c-.55 0-1 .45-1 1s.45 1 1 1zM3 7c0 .55.45 1 1 1h16c.55 0 1-.45 1-1s-.45-1-1-1H4c-.55 0-1 .45-1 1z"/></svg>
    </div>
  </a>
</div>
          <span id="navMenuMobile" class="hide">
  <button id="navCollapseBtn" class="navbar__dropdown--title" aria-label="Section Menu" data-dir="ltr">
    
      react
    
    <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"><path fill="currentColor" d="M8.12 9.29L12 13.17l3.88-3.88c.39-.39 1.02-.39 1.41 0 .39.39.39 1.02 0 1.41l-4.59 4.59c-.39.39-1.02.39-1.41 0L6.7 10.7c-.39-.39-.39-1.02 0-1.41.39-.38 1.03-.39 1.42 0z"/></svg>
  </button>
  <div class="navbar__collapse" data-open="false">
  <ul>
    

  
  
  
  
  <li class="navbar__menu--mobile-item ">
    <a href="/javascript" class="">JavaScript</a>
  </li>

  
  
  
  
  <li class="navbar__menu--mobile-item ">
    <a href="/nodejs" class="">Nodejs</a>
  </li>

  
  
  
  
  <li class="navbar__menu--mobile-item active">
    <a href="/react" class="active">React</a>
  </li>

  
  
  
  
  <li class="navbar__menu--mobile-item ">
    <a href="/react-patterns" class="">React-Patterns</a>
  </li>

  
  
  
  
  <li class="navbar__menu--mobile-item ">
    <a href="/react-native" class="">React-Native</a>
  </li>

  
  
  
  
  <li class="navbar__menu--mobile-item ">
    <a href="/rust" class="">Rust</a>
  </li>

  
  
  
  
  <li class="navbar__menu--mobile-item ">
    <a href="/others" class="">Others</a>
  </li>

  </ul>
</div>
</span>
          <ul id="navMenu" class="navbar__menu">
            

  
  
  
  
  <li class="navbar__menu--item ">
    <a href="/javascript" data-bgimg="false">JavaScript</a>
  </li>

  
  
  
  
  <li class="navbar__menu--item ">
    <a href="/nodejs" data-bgimg="false">Nodejs</a>
  </li>

  
  
  
  
  <li class="navbar__menu--item active">
    <a href="/react" data-bgimg="false">React</a>
  </li>

  
  
  
  
  <li class="navbar__menu--item ">
    <a href="/react-patterns" data-bgimg="false">React-Patterns</a>
  </li>

  
  
  
  
  <li class="navbar__menu--item ">
    <a href="/react-native" data-bgimg="false">React-Native</a>
  </li>

  
  
  
  
  <li class="navbar__menu--item ">
    <a href="/rust" data-bgimg="false">Rust</a>
  </li>

  
  
  
  
  <li class="navbar__menu--item ">
    <a href="/others" data-bgimg="false">Others</a>
  </li>

          </ul>
        </div>
        <div class="grow"></div>
        <div id="siteSearch">
          

<div class="search" data-bgimg="false">
  <span class="icon">
    <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" fill="currentColor" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M15.5 14h-.79l-.28-.27c1.2-1.4 1.82-3.31 1.48-5.34-.47-2.78-2.79-5-5.59-5.34-4.23-.52-7.79 3.04-7.27 7.27.34 2.8 2.56 5.12 5.34 5.59 2.03.34 3.94-.28 5.34-1.48l.27.28v.79l4.25 4.25c.41.41 1.08.41 1.49 0 .41-.41.41-1.08 0-1.49L15.5 14zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
  </span>
  <input id="search" aria-label="Site Search" class="input" type="text" autocomplete="off" placeholder="Search">
  <div id="search-results" class="dropdown">
    <div id="search-menu" class="dropdown-menu">
    </div>
  </div>
</div>


        </div>
        <div id="mobileSearch" class="hide">
          
<div id="mobileSearchBtn" class="mobile-search__btn">
  <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" fill="currentColor" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M15.5 14h-.79l-.28-.27c1.2-1.4 1.82-3.31 1.48-5.34-.47-2.78-2.79-5-5.59-5.34-4.23-.52-7.79 3.04-7.27 7.27.34 2.8 2.56 5.12 5.34 5.59 2.03.34 3.94-.28 5.34-1.48l.27.28v.79l4.25 4.25c.41.41 1.08.41 1.49 0 .41-.41.41-1.08 0-1.49L15.5 14zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
</div>

<div id="search-mobile-container" class="mobile-search hide">
  <div class="mobile-search__top">
    <input id="search-mobile" type="text" aria-label="Mobile Search" placeholder="Search..." class="mobile-search__top--input"/>
    <div id="search-mobile-close" class="mobile-search__top--icon">
      <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path opacity=".87" fill="none" d="M0 0h24v24H0V0z"/><path fill="currentColor" d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm3.59-13L12 10.59 8.41 7 7 8.41 10.59 12 7 15.59 8.41 17 12 13.41 15.59 17 17 15.59 13.41 12 17 8.41z"/></svg>
    </div>
  </div>
  <div id="search-mobile-results" class="mobile-search__body">
    
  </div>
</div>

        </div>
        <div class="navbar__icons">
  
  
<button id="toggleToDark" class="navbar__icons--icon" aria-label="Toggle to dark mode">
  <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none" /><path fill="currentColor" d="M20 15.31L23.31 12 20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69zM12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6z"/></svg>
</button>
<button id="toggleToLight" class="navbar__icons--icon" aria-label="Toggle to light mode">
  <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path fill="currentColor" d="M20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12 20 8.69zM12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6zm0-10c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4z"/></svg>
</button>
<script>
  var theme = localStorage.getItem('theme');
  var toggleToLightBtn = document.getElementById('toggleToLight');
  var toggleToDarkBtn = document.getElementById('toggleToDark');

  if (theme && theme === 'dark') {
    toggleToLightBtn.className = 'navbar__icons--icon';
    toggleToDarkBtn.className = 'hide';
  } else {
    toggleToLightBtn.className = 'hide';
    toggleToDarkBtn.className = 'navbar__icons--icon';
  }
</script>

  <div class="navbar__icons--icon">
    <a href="https://github.com/codthing" title="Github repository" aria-label="Github repository" target="_blank" rel="noreferrer">
    <svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="22" height="22"><path d="M10.9,2.1c-4.6,0.5-8.3,4.2-8.8,8.7c-0.5,4.7,2.2,8.9,6.3,10.5C8.7,21.4,9,21.2,9,20.8v-1.6c0,0-0.4,0.1-0.9,0.1 c-1.4,0-2-1.2-2.1-1.9c-0.1-0.4-0.3-0.7-0.6-1C5.1,16.3,5,16.3,5,16.2C5,16,5.3,16,5.4,16c0.6,0,1.1,0.7,1.3,1c0.5,0.8,1.1,1,1.4,1 c0.4,0,0.7-0.1,0.9-0.2c0.1-0.7,0.4-1.4,1-1.8c-2.3-0.5-4-1.8-4-4c0-1.1,0.5-2.2,1.2-3C7.1,8.8,7,8.3,7,7.6c0-0.4,0-0.9,0.2-1.3 C7.2,6.1,7.4,6,7.5,6c0,0,0.1,0,0.1,0C8.1,6.1,9.1,6.4,10,7.3C10.6,7.1,11.3,7,12,7s1.4,0.1,2,0.3c0.9-0.9,2-1.2,2.5-1.3 c0,0,0.1,0,0.1,0c0.2,0,0.3,0.1,0.4,0.3C17,6.7,17,7.2,17,7.6c0,0.8-0.1,1.2-0.2,1.4c0.7,0.8,1.2,1.8,1.2,3c0,2.2-1.7,3.5-4,4 c0.6,0.5,1,1.4,1,2.3v2.6c0,0.3,0.3,0.6,0.7,0.5c3.7-1.5,6.3-5.1,6.3-9.3C22,6.1,16.9,1.4,10.9,2.1z"/></svg>
    </a>
  </div>
</div>


      </div>
    </div>
  </div>
</nav>

<script>
  var siteLogo = document.getElementById('siteLogo');
  var tabletLogo = document.getElementById('tabletLogo');
  var mobileLogo = document.getElementById('mobileLogo');
  var navMenu = document.getElementById('navMenu');
  var navMenuMobile = document.getElementById('navMenuMobile');
  var siteSearch = document.getElementById('siteSearch');
  var mobileSearch = document.getElementById('mobileSearch');
  
  enquire.register("screen and (max-width:1280px)", {
    match: function () {
      siteLogo.className = 'navbar__logo--wrapper';
      tabletLogo.className = 'hide';
      mobileLogo.className = 'hide';
      navMenu.className = 'navbar__menu';
      navMenuMobile.className = 'hide';
      siteSearch.className = '';
      mobileSearch.className = 'hide';
    },
    unmatch: function () {
      siteLogo.className = 'navbar__logo--wrapper';
      tabletLogo.className = 'hide';
      mobileLogo.className = 'hide';
      navMenu.className = 'navbar__menu';
      navMenuMobile.className = 'hide';
      siteSearch.className = '';
      mobileSearch.className = 'hide';
    },
  }).register("screen and (max-width:960px)", {
    match: function () {
      siteLogo.className = 'hide';
      tabletLogo.className = '';
      mobileLogo.className = 'hide';
      navMenu.className = 'navbar__menu';
      navMenuMobile.className = 'hide';
      siteSearch.className = 'hide';
      mobileSearch.className = 'navbar__icons--icon';
    },
    unmatch: function () {
      siteLogo.className = 'navbar__logo--wrapper';
      tabletLogo.className = 'hide';
      mobileLogo.className = 'hide';
      navMenu.className = 'navbar__menu';
      navMenuMobile.className = 'hide';
      siteSearch.className = '';
      mobileSearch.className = 'hide';
    },
  }).register("screen and (max-width:600px)", {
    match: function () {
      siteLogo.className = 'hide';
      tabletLogo.className = 'hide';
      mobileLogo.className = '';
      navMenu.className = 'hide';
      navMenuMobile.className = '';
      siteSearch.className = 'hide';
      mobileSearch.className = 'navbar__icons--icon';
    },
    unmatch: function () {
      siteLogo.className = 'hide';
      tabletLogo.className = '';
      mobileLogo.className = 'hide';
      navMenu.className = 'navbar__menu';
      navMenuMobile.className = 'hide';
      siteSearch.className = 'hide';
      mobileSearch.className = 'navbar__icons--icon';
    },
  });
</script>

        
  <div class="top">
  <div class="header__wrapper bgcolor__header">
    <div class="divider">
      <div class="lmr">
        <h2 class="title">Hooks 最佳实践</h2>
      </div>
    </div>
  </div>

  <div class="header__wrapper bgcolor__breadcrumb">
    <div class="divider">
      <div class="lmr flexbox">
        
          <nav class="breadcrumb" aria-label="breadcrumbs" data-is-blog="false">
  
  <ol>
    
  
  
  
  
  
  <li >
    
      <a href="https://codthing.github.io/" class="capitalize">codthing</a>
    
  </li>
  
  
  <li >
    
      <a href="https://codthing.github.io/react/" class="capitalize">React</a>
    
  </li>
  
  
  <li  class="is-active" >
    
      <span>Hooks 最佳实践</span>
    
  </li>
  
  </ol>
  
</nav>
        
        <div class="grow"></div>
        
  <label class="switch switch__rel">
    
      <input id="toggle-sidebar" aria-label="Toggle Sidebar Visibility" type="checkbox" checked />
      <span class="slider">
        <span class="slider__icon ">
          <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path fill="currentColor" d="M5 13h11.17l-4.88 4.88c-.39.39-.39 1.03 0 1.42.39.39 1.02.39 1.41 0l6.59-6.59c.39-.39.39-1.02 0-1.41l-6.58-6.6c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.02 0 1.41L16.17 11H5c-.55 0-1 .45-1 1s.45 1 1 1z"/></svg>
        </span>
        <span class="slider__icon hide">
          <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path fill="currentColor" d="M19 11H7.83l4.88-4.88c.39-.39.39-1.03 0-1.42-.39-.39-1.02-.39-1.41 0l-6.59 6.59c-.39.39-.39 1.02 0 1.41l6.59 6.59c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L7.83 13H19c.55 0 1-.45 1-1s-.45-1-1-1z"/></svg>
        </span>
      </span>
    
    
  </label>

        
      </div>
    </div>
  </div>
</div>

        
  

<div class="mid">
  <div class="divider">
    
    <nav id="single-menu" class="l" data-dir="ltr">
      
        <div class="menu">
  <h6 class="menu__label">
    
      Hooks 最佳实践
    
  </h6>
  <ul>
    
    
      
  
  
  
  
  
  
  

  
  
  
  
  
  
  
  <li class="">
    <a href="https://codthing.github.io/react/redux/react-redux/"
      class="menu__title ">React &amp; Redux</a>
  </li>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  
  
  <li class="">
    <a href="https://codthing.github.io/react/react/react-base/"
      class="menu__title ">React 基础</a>
  </li>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  
  
  <li class="">
    <a href="https://codthing.github.io/react/react/react-bp/"
      class="menu__title ">React 最佳实践</a>
  </li>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  
  
  <li class="">
    <a href="https://codthing.github.io/react/redux/redux-base/"
      class="menu__title ">Redux</a>
  </li>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  
  
  <li class="active">
    <a href="https://codthing.github.io/react/hooks/hooks-bp/"
      class="menu__title active">Hooks 最佳实践</a>
  </li>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  
  
  <li class="">
    <a href="https://codthing.github.io/react/redux/hooks-redux/"
      class="menu__title ">Hooks &amp; Redux</a>
  </li>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  
  
  <li class="">
    <a href="https://codthing.github.io/react/zustand/zustand-base/"
      class="menu__title ">zustand</a>
  </li>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  
  
  <li class="">
    <a href="https://codthing.github.io/react/recoil/recoil-bp/"
      class="menu__title ">Recoil 最佳实践</a>
  </li>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  <li class="menu__title--collapse " data-depth="0" data-permalink="https://codthing.github.io/react/next/">
    <span class="menu__title--collapse-text">
      Next.js
    </span>
    <span class="menu__title--icon right">
      
        <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path fill="currentColor" d="M9.29 15.88L13.17 12 9.29 8.12c-.39-.39-.39-1.02 0-1.41.39-.39 1.02-.39 1.41 0l4.59 4.59c.39.39.39 1.02 0 1.41L10.7 17.3c-.39.39-1.02.39-1.41 0-.38-.39-.39-1.03 0-1.42z"/></svg>
      
    </span>
  </li>
  <ul class="menu__list " data-data=/next/ data-link=https://codthing.github.io/react/hooks/hooks-bp/>
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/next/next-base/" class="menu__title " data-depth="0">Next.js 速览</a>
        </li>
      
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/next/next-content/" class="menu__title " data-depth="0">Next.js 核心工作内容</a>
        </li>
      
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/next/next-multiple/" class="menu__title " data-depth="0">Next.js 各种组合</a>
        </li>
      
    
  </ul>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  
  
  <li class="">
    <a href="https://codthing.github.io/react/remix/remix-base/"
      class="menu__title ">Remix 基础</a>
  </li>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  
  
  <li class="">
    <a href="https://codthing.github.io/react/remix/remix-adv/"
      class="menu__title ">Remix 进阶</a>
  </li>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  <li class="menu__title--collapse " data-depth="0" data-permalink="https://codthing.github.io/react/storybook/">
    <span class="menu__title--collapse-text">
      Storybook
    </span>
    <span class="menu__title--icon right">
      
        <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path fill="currentColor" d="M9.29 15.88L13.17 12 9.29 8.12c-.39-.39-.39-1.02 0-1.41.39-.39 1.02-.39 1.41 0l4.59 4.59c.39.39.39 1.02 0 1.41L10.7 17.3c-.39.39-1.02.39-1.41 0-.38-.39-.39-1.03 0-1.42z"/></svg>
      
    </span>
  </li>
  <ul class="menu__list " data-data=/storybook/ data-link=https://codthing.github.io/react/hooks/hooks-bp/>
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/storybook/sb-bp/" class="menu__title " data-depth="0">Storybook 最佳实践</a>
        </li>
      
    
      
        
  
  
  
  
  
  
  

  
  
  
  
  
  <li class="menu__title--collapse " data-depth="1" data-permalink="https://codthing.github.io/react/storybook/sb-started/">
    <span class="menu__title--collapse-text">
      开始
    </span>
    <span class="menu__title--icon right">
      
        <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path fill="currentColor" d="M9.29 15.88L13.17 12 9.29 8.12c-.39-.39-.39-1.02 0-1.41.39-.39 1.02-.39 1.41 0l4.59 4.59c.39.39.39 1.02 0 1.41L10.7 17.3c-.39.39-1.02.39-1.41 0-.38-.39-.39-1.03 0-1.42z"/></svg>
      
    </span>
  </li>
  <ul class="menu__list " data-data=/sb-started/ data-link=https://codthing.github.io/react/hooks/hooks-bp/>
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/storybook/sb-started/sb-introduction/" class="menu__title " data-depth="1">介绍</a>
        </li>
      
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/storybook/sb-started/sb-install/" class="menu__title " data-depth="1">安装</a>
        </li>
      
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/storybook/sb-started/sb-what/" class="menu__title " data-depth="1">Story 是什么？</a>
        </li>
      
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/storybook/sb-started/sb-browse/" class="menu__title " data-depth="1">浏览故事</a>
        </li>
      
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/storybook/sb-started/sb-setup/" class="menu__title " data-depth="1">设置</a>
        </li>
      
    
  </ul>
  

      
    
      
        
  
  
  
  
  
  
  

  
  
  
  
  
  <li class="menu__title--collapse " data-depth="1" data-permalink="https://codthing.github.io/react/storybook/sb-write/">
    <span class="menu__title--collapse-text">
      写故事
    </span>
    <span class="menu__title--icon right">
      
        <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path fill="currentColor" d="M9.29 15.88L13.17 12 9.29 8.12c-.39-.39-.39-1.02 0-1.41.39-.39 1.02-.39 1.41 0l4.59 4.59c.39.39.39 1.02 0 1.41L10.7 17.3c-.39.39-1.02.39-1.41 0-.38-.39-.39-1.03 0-1.42z"/></svg>
      
    </span>
  </li>
  <ul class="menu__list " data-data=/sb-write/ data-link=https://codthing.github.io/react/hooks/hooks-bp/>
    
  </ul>
  

      
    
      
        
  
  
  
  
  
  
  

  
  
  
  
  
  <li class="menu__title--collapse " data-depth="1" data-permalink="https://codthing.github.io/react/storybook/sb-test/">
    <span class="menu__title--collapse-text">
      编写测试
    </span>
    <span class="menu__title--icon right">
      
        <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path fill="currentColor" d="M9.29 15.88L13.17 12 9.29 8.12c-.39-.39-.39-1.02 0-1.41.39-.39 1.02-.39 1.41 0l4.59 4.59c.39.39.39 1.02 0 1.41L10.7 17.3c-.39.39-1.02.39-1.41 0-.38-.39-.39-1.03 0-1.42z"/></svg>
      
    </span>
  </li>
  <ul class="menu__list " data-data=/sb-test/ data-link=https://codthing.github.io/react/hooks/hooks-bp/>
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/storybook/sb-test/sb-test-introduction/" class="menu__title " data-depth="1">介绍</a>
        </li>
      
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/storybook/sb-test/sb-visual-tests/" class="menu__title " data-depth="1">视觉测试</a>
        </li>
      
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/storybook/sb-test/sb-accessibility-testing/" class="menu__title " data-depth="1">可访问性测试</a>
        </li>
      
    
  </ul>
  

      
    
  </ul>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  <li class="menu__title--collapse " data-depth="0" data-permalink="https://codthing.github.io/react/tailwindcss/">
    <span class="menu__title--collapse-text">
      tailwindcss
    </span>
    <span class="menu__title--icon right">
      
        <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path fill="currentColor" d="M9.29 15.88L13.17 12 9.29 8.12c-.39-.39-.39-1.02 0-1.41.39-.39 1.02-.39 1.41 0l4.59 4.59c.39.39.39 1.02 0 1.41L10.7 17.3c-.39.39-1.02.39-1.41 0-.38-.39-.39-1.03 0-1.42z"/></svg>
      
    </span>
  </li>
  <ul class="menu__list " data-data=/tailwindcss/ data-link=https://codthing.github.io/react/hooks/hooks-bp/>
    
      
        
  
  
  
  
  
  
  

  
  
  
  
  
  <li class="menu__title--collapse " data-depth="1" data-permalink="https://codthing.github.io/react/tailwindcss/tailwindcss-start.md/">
    <span class="menu__title--collapse-text">
      开始
    </span>
    <span class="menu__title--icon right">
      
        <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path fill="currentColor" d="M9.29 15.88L13.17 12 9.29 8.12c-.39-.39-.39-1.02 0-1.41.39-.39 1.02-.39 1.41 0l4.59 4.59c.39.39.39 1.02 0 1.41L10.7 17.3c-.39.39-1.02.39-1.41 0-.38-.39-.39-1.03 0-1.42z"/></svg>
      
    </span>
  </li>
  <ul class="menu__list " data-data=/tailwindcss-start.md/ data-link=https://codthing.github.io/react/hooks/hooks-bp/>
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/tailwindcss/tailwindcss-start.md/tailwindcss-install/" class="menu__title " data-depth="1">安装</a>
        </li>
      
    
  </ul>
  

      
    
  </ul>
  

    
      
  
  
  
  
  
  
  

  
  
  
  
  
  <li class="menu__title--collapse " data-depth="0" data-permalink="https://codthing.github.io/react/wechat-mini/">
    <span class="menu__title--collapse-text">
      小程序开发
    </span>
    <span class="menu__title--icon right">
      
        <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path fill="currentColor" d="M9.29 15.88L13.17 12 9.29 8.12c-.39-.39-.39-1.02 0-1.41.39-.39 1.02-.39 1.41 0l4.59 4.59c.39.39.39 1.02 0 1.41L10.7 17.3c-.39.39-1.02.39-1.41 0-.38-.39-.39-1.03 0-1.42z"/></svg>
      
    </span>
  </li>
  <ul class="menu__list " data-data=/wechat-mini/ data-link=https://codthing.github.io/react/hooks/hooks-bp/>
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/wechat-mini/taro-log/" class="menu__title " data-depth="0">Taro开发日志</a>
        </li>
      
    
      
        
        
        <li>
          <a href="https://codthing.github.io/react/wechat-mini/remax-log/" class="menu__title " data-depth="0">Remax开发日志</a>
        </li>
      
    
  </ul>
  

    
  </ul>
</div>


      
    </nav>

    <article id="list-main" class="m" data-dir="ltr">
      <div class="single" data-display="block">
  

  <article class="single__contents" data-dir="ltr">
    <p>Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。</p>
<p>Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。</p>
<p>Hook 使你在无需修改组件结构的情况下复用状态逻辑。 这使得在组件间或社区内共享 Hook 变得更便捷。</p>
<p>Hook 将组件中相互关联的部分拆分成更小的函数（比如设置订阅或请求数据），而并非强制按照生命周期划分。你还可以使用 reducer 来管理组件的内部状态，使其更加可预测。</p>
<p><strong>什么时候我会用 Hook？</strong> 如果你在编写函数组件并意识到需要向其添加一些 state，以前的做法是必须将其转化为 class。现在你可以在现有的函数组件中使用 Hook。</p>
<h2 id="一hooks-底层原理">一、Hooks 底层原理</h2>
<p>React 是如何把对 Hook 的调用和组件联系起来的？</p>
<p>React 保持对当前渲染中的组件的追踪。多亏了 <code>Hook 规范</code>，我们得知 Hook 只会在 React 组件中被调用（或自定义 Hook —— 同样只会在 React 组件中被调用）。</p>
<p>每个组件内部都有一个<code>「记忆单元格」</code>列表。它们只不过是我们用来存储一些数据的 JavaScript 对象。</p>
<p>当你用 useState() 调用一个 Hook 的时候，它会读取当前的单元格（或在首次渲染时将其初始化），然后把指针移动到下一个。</p>
<p>这就是多个 useState() 调用会得到各自独立的本地 state 的原因。</p>
<h2 id="二hooks-规范">二、Hooks 规范</h2>
<h3 id="1-只在最顶层使用-hook">1. 只在最顶层使用 Hook</h3>
<h5 id="不要在循环条件或嵌套函数中调用-hook-确保总是在你的-react-函数的最顶层以及任何-return-之前调用他们遵守这条规则你就能确保-hook-在每一次渲染中都按照同样的顺序被调用">不要在循环，条件或嵌套函数中调用 Hook ,确保总是在你的 React 函数的最顶层以及任何 return 之前调用他们。遵守这条规则，你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。</h5>
<p>示例：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">function Form() {
  // 1. 使用`name state`变量
  const [name, setName] = useState(&#39;Mary&#39;);

  // 2. 使用 `effect` 来持久化表单
  useEffect(function persistForm() {
    localStorage.setItem(&#39;formData&#39;, name);
  });

  // 3. 使用 `surname` 状态变量
  const [surname, setSurname] = useState(&#39;Poppins&#39;);

  // 4. 使用 `effect` 来更新标题
  useEffect(function updateTitle() {
    document.title = name + &#39; &#39; + surname;
  });

  // ...
}
</code></pre><p>那么 React 怎么知道哪个 state 对应哪个 useState？答案是 React 靠的是 Hook 调用的顺序。因为示例中，Hook 的调用顺序在每次渲染中都是相同的，所以它能够正常工作：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">// ------------
// 首次渲染
// ------------
useState(&#39;Mary&#39;)           // 1. 使用 &#39;Mary&#39; 初始化变量名为 name 的 state
useEffect(persistForm)     // 2. 添加 effect 以保存 form 操作
useState(&#39;Poppins&#39;)        // 3. 使用 &#39;Poppins&#39; 初始化变量名为 surname 的 state
useEffect(updateTitle)     // 4. 添加 effect 以更新标题

// -------------
// 二次渲染
// -------------
useState(&#39;Mary&#39;)           // 1. 读取变量名为 name 的 state（参数被忽略）
useEffect(persistForm)     // 2. 替换保存 form 的 effect
useState(&#39;Poppins&#39;)        // 3. 读取变量名为 surname 的 state（参数被忽略）
useEffect(updateTitle)     // 4. 替换更新标题的 effect

// ...
</code></pre><p>只要 Hook 的调用顺序在多次渲染之间保持一致，React 就能正确地将内部 state 和对应的 Hook 进行关联。</p>
<h5 id="如果将一个-hook-调用放到一个条件语句中会发生什么呢">如果将一个 Hook 调用放到一个条件语句中会发生什么呢？</h5>
<p>(例如 persistForm effect)</p>
<pre tabindex="0"><code class="language-base" data-lang="base">// 🔴 在条件语句中使用 Hook 违反第一条规则
  if (name !== &#39;&#39;) {
    useEffect(function persistForm() {
      localStorage.setItem(&#39;formData&#39;, name);
    });
  }
</code></pre><p>在第一次渲染中 name !== &rsquo;&rsquo; 这个条件值为 true，所以我们会执行这个 Hook。但是下一次渲染时我们可能清空了表单，表达式值变为 false。此时的渲染会跳过该 Hook，Hook 的调用顺序发生了改变：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">useState(&#39;Mary&#39;)           // 1. 读取变量名为 name 的 state（参数被忽略）
// useEffect(persistForm)  // 🔴 此 Hook 被忽略！
useState(&#39;Poppins&#39;)        // 🔴 2 （之前为 3）。读取变量名为 surname 的 state 失败
useEffect(updateTitle)     // 🔴 3 （之前为 4）。替换更新标题的 effect 失败
</code></pre><p>React 不知道第二个 useState 的 Hook 应该返回什么。React 会以为在该组件中第二个 Hook 的调用像上次的渲染一样，对应的是 persistForm 的 effect，但并非如此。从这里开始，后面的 Hook 调用都被提前执行，导致 bug 的产生。</p>
<h5 id="这就是为什么-hook-需要在我们组件的最顶层调用如果我们想要有条件地执行一个-effect可以将判断放到-hook-的内部">这就是为什么 Hook 需要在我们组件的最顶层调用。如果我们想要有条件地执行一个 effect，可以将判断放到 Hook 的内部：</h5>
<pre tabindex="0"><code class="language-base" data-lang="base">useEffect(function persistForm() {
    // 👍 将条件判断放置在 effect 中
    if (name !== &#39;&#39;) {
      localStorage.setItem(&#39;formData&#39;, name);
    }
  });
</code></pre><h3 id="2-只在-react-函数中调用-hook">2. 只在 React 函数中调用 Hook</h3>
<p>不要在普通的 JavaScript 函数中调用 Hook。你可以：</p>
<p>✅ 在 React 的函数组件中调用 Hook<br />
✅ 在自定义 Hook 中调用其他 Hook (我们将会在下一页 中学习这个。)</p>
<p>遵循此规则，确保组件的状态逻辑在代码中清晰可见。</p>
<h2 id="三自定义-hook">三、自定义 Hook</h2>
<p>自定义 Hook 是一种自然遵循 Hook 设计的约定，而并不是 React 的特性。</p>
<p>自定义 Hook 是一种重用状态逻辑的机制(例如设置为订阅并存储当前值)，所以每次使用自定义 Hook 时，其中的所有 state 和副作用都是完全隔离的。</p>
<p>通过自定义 Hook，可以将组件逻辑提取到可重用的函数中。</p>
<p>这是一个聊天程序中的组件FriendListItem ，该组件用于显示好友的在线状态：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">import React, { useState, useEffect } from &#39;react&#39;;

function FriendListItem(props) {
----------------------------------------------------
  const [isOnline, setIsOnline] = useState(null);
  useEffect(() =&gt; {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () =&gt; {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });
----------------------------------------------------
  return (
    &lt;li style={{ color: isOnline ? &#39;green&#39; : &#39;black&#39; }}&gt;
      {props.friend.name}
    &lt;/li&gt;
  );
}
</code></pre><h3 id="1-提取自定义-hook">1. 提取自定义 Hook</h3>
<p>当想在两个函数之间共享逻辑时，把它提取到第三个函数中。而组件和 Hook 都是函数，所以也同样适用这种方式。</p>
<p>自定义 Hook 是一个函数，其名称以 <code>use</code> 开头，函数内部可以调用其他的 Hook。</p>
<p>例如，下面的 useFriendStatus 是一个自定义的 Hook:</p>
<pre tabindex="0"><code class="language-base" data-lang="base">import { useState, useEffect } from &#39;react&#39;;

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  useEffect(() =&gt; {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () =&gt; {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}
</code></pre><p>此处并未包含任何新的内容——逻辑是从上述组件拷贝来的（<code>记得去掉props</code>）。与组件中一致，请确保只在自定义 Hook 的顶层无条件地调用其他 Hook。</p>
<h3 id="2-使用自定义-hook">2. 使用自定义 Hook</h3>
<p>把这个逻辑提取到 FriendListItem 的自定义 Hook 中，然后就可以使用它了：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">function FriendListItem(props) {
------------------------------------------
  const isOnline = useFriendStatus(props.friend.id);
------------------------------------------
  return (
    &lt;li style={{ color: isOnline ? &#39;green&#39; : &#39;black&#39; }}&gt;
      {props.friend.name}
    &lt;/li&gt;
  );
}
</code></pre><h3 id="3-在多个-hook-之间传递信息">3. 在多个 Hook 之间传递信息</h3>
<p>由于 Hook 本身就是函数，因此可以在它们之间传递信息。</p>
<p>这是一个聊天消息接收者的选择器，它会显示当前选定的好友是否在线:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">friendList</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span> <span class="nx">id</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;Phoebe&#39;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span> <span class="nx">id</span><span class="o">:</span> <span class="mi">2</span><span class="p">,</span> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;Rachel&#39;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span> <span class="nx">id</span><span class="o">:</span> <span class="mi">3</span><span class="p">,</span> <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;Ross&#39;</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">ChatRecipientPicker</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="o">---------------------------------------------------------</span>
</span></span><span class="line"><span class="cl"><span class="c1">//当前选择的好友 ID 保存在 recipientID 状态变量中，并在用户从 &lt;select&gt; 中选择其他好友时更新这个 state。
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">const</span> <span class="p">[</span><span class="nx">recipientID</span><span class="p">,</span> <span class="nx">setRecipientID</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> 
</span></span><span class="line"><span class="cl"><span class="c1">//由于 useState 为我们提供了 recipientID 状态变量的最新值，
</span></span></span><span class="line"><span class="cl"><span class="c1">//因此我们可以将它作为参数传递给自定义的 useFriendStatus Hook：
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">const</span> <span class="nx">isRecipientOnline</span> <span class="o">=</span> <span class="nx">useFriendStatus</span><span class="p">(</span><span class="nx">recipientID</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">---------------------------------------------------------</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="o">&lt;&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">---------------------------------------------------------</span>
</span></span><span class="line"><span class="cl"><span class="c1">//当我们选择不同的好友并更新 recipientID 状态变量时，
</span></span></span><span class="line"><span class="cl"><span class="c1">//useFriendStatus Hook 将会取消订阅之前选中的好友，并订阅新选中的好友状态。
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="o">&lt;</span><span class="nx">Circle</span> <span class="nx">color</span><span class="o">=</span><span class="p">{</span><span class="nx">isRecipientOnline</span> <span class="o">?</span> <span class="s1">&#39;green&#39;</span> <span class="o">:</span> <span class="s1">&#39;red&#39;</span><span class="p">}</span> <span class="o">/&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">---------------------------------------------------------</span>
</span></span><span class="line"><span class="cl">      <span class="o">&lt;</span><span class="nx">select</span>
</span></span><span class="line"><span class="cl">        <span class="nx">value</span><span class="o">=</span><span class="p">{</span><span class="nx">recipientID</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="nx">onChange</span><span class="o">=</span><span class="p">{</span><span class="nx">e</span> <span class="p">=&gt;</span> <span class="nx">setRecipientID</span><span class="p">(</span><span class="nb">Number</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">value</span><span class="p">))}</span>
</span></span><span class="line"><span class="cl">      <span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span><span class="nx">friendList</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">friend</span> <span class="p">=&gt;</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="o">&lt;</span><span class="nx">option</span> <span class="nx">key</span><span class="o">=</span><span class="p">{</span><span class="nx">friend</span><span class="p">.</span><span class="nx">id</span><span class="p">}</span> <span class="nx">value</span><span class="o">=</span><span class="p">{</span><span class="nx">friend</span><span class="p">.</span><span class="nx">id</span><span class="p">}</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span><span class="nx">friend</span><span class="p">.</span><span class="nx">name</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">          <span class="o">&lt;</span><span class="err">/option&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">))}</span>
</span></span><span class="line"><span class="cl">      <span class="o">&lt;</span><span class="err">/select&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">&lt;</span><span class="err">/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="4-自定义hooks中使用usereducer">4. 自定义Hooks中使用useReducer</h3>
<p>自定义 Hook 解决了以前在 React 组件中无法灵活共享逻辑的问题。更重要的是，创建自定义 Hook 就像使用 React 内置的功能一样简单。</p>
<p>尽量避免过早地增加抽象逻辑。既然函数组件能够做的更多，那么代码库中函数组件的代码行数可能会剧增。<br />
这属于正常现象 —— 不必立即将它们拆分为 Hook。但我们仍鼓励你能通过自定义 Hook 寻找可能，以达到简化代码逻辑，解决组件杂乱无章的目的。</p>
<p>例如，有个复杂的组件，其中包含了大量以特殊的方式来管理的内部状态。useState 并不会使得集中更新逻辑变得容易，因此你可能更愿意使用 redux 中的 reducer 来编写。</p>
<p>那么，为什么我们不编写一个 useReducer 的 Hook，使用 reducer 的方式来管理组件的内部 state 呢？其简化版本可能如下所示：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">function useReducer(reducer, initialState) {
  const [state, setState] = useState(initialState);

  function dispatch(action) {
    const nextState = reducer(state, action);
    setState(nextState);
  }

  return [state, dispatch];
}
</code></pre><p>在组件中使用它，让 reducer 驱动它管理 state：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">function Todos() {
  const [todos, dispatch] = useReducer(todosReducer, []);

  function handleAddClick(text) {
    dispatch({ type: &#39;add&#39;, text });
  }

  // ...
}
</code></pre><h3 id="5-异步-fetch-hooks">5. 异步 fetch Hooks</h3>
<p>处理异步调用，应该包含以下内容：</p>
<ul>
<li>创建一个接受处理函数的自定义钩子，<code>fn</code>.</li>
<li>为自定义hooks的状态定义一个 <code>reducer</code> 函数和一个初始状态。</li>
<li>使用<code>useReducer()</code>钩子初始化<code>state</code>变量和<code>dispatch</code>函数。</li>
<li>定义一个异步 <code>run</code> 函数，该函数将运行提供的<code>回调 fn</code>，同时根据需要使用 <code>dispatch</code> 更新 <code>state</code>。</li>
<li>返回一个包含<code>state</code>( <code>value</code>,<code>error</code>和<code>loading</code>) 和<code>run</code>函数的对象。</li>
</ul>
<h4 id="1-封装-useasync">1). 封装 useAsync</h4>
<ul>
<li><code>./customHooks/useAsync.ts</code></li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">React</span><span class="p">,</span> <span class="p">{</span> <span class="nx">useReducer</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;react&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">useAsync</span> <span class="o">=</span> <span class="nx">fn</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="c1">//创建一个接受处理函数的自定义钩子，`fn`.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">const</span> <span class="nx">initialState</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">loading</span>: <span class="kt">false</span><span class="p">,</span> <span class="nx">error</span>: <span class="kt">null</span><span class="p">,</span> <span class="nx">value</span>: <span class="kt">null</span> <span class="p">};</span> <span class="c1">//初始化状态
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">const</span> <span class="nx">stateReducer</span> <span class="o">=</span> <span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">action</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span><span class="c1">//处理状态的逻辑
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">switch</span> <span class="p">(</span><span class="nx">action</span><span class="p">.</span><span class="kr">type</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">case</span> <span class="s1">&#39;start&#39;</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">{</span> <span class="nx">loading</span>: <span class="kt">true</span><span class="p">,</span> <span class="nx">error</span>: <span class="kt">null</span><span class="p">,</span> <span class="nx">value</span>: <span class="kt">null</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">      <span class="k">case</span> <span class="s1">&#39;finish&#39;</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">{</span> <span class="nx">loading</span>: <span class="kt">false</span><span class="p">,</span> <span class="nx">error</span>: <span class="kt">null</span><span class="p">,</span> <span class="nx">value</span>: <span class="kt">action.value</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">      <span class="k">case</span> <span class="s1">&#39;error&#39;</span><span class="o">:</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">{</span> <span class="nx">loading</span>: <span class="kt">false</span><span class="p">,</span> <span class="nx">error</span>: <span class="kt">action.error</span><span class="p">,</span> <span class="nx">value</span>: <span class="kt">null</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">[</span><span class="nx">state</span><span class="p">,</span> <span class="nx">dispatch</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useReducer</span><span class="p">(</span><span class="nx">stateReducer</span><span class="p">,</span> <span class="nx">initialState</span><span class="p">);</span><span class="c1">//使用`useReducer()`钩子初始化`state`变量和`dispatch`函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">run</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">(</span><span class="nx">args</span> <span class="o">=</span> <span class="kc">null</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span><span class="c1">//定义一个异步 `run` 函数，该函数将运行提供的`回调 fn`，同时根据需要使用 `dispatch` 更新 `state`
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">dispatch</span><span class="p">({</span> <span class="kr">type</span><span class="o">:</span> <span class="s1">&#39;start&#39;</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">value</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fn</span><span class="p">(</span><span class="nx">args</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="nx">dispatch</span><span class="p">({</span> <span class="kr">type</span><span class="o">:</span> <span class="s1">&#39;finish&#39;</span><span class="p">,</span> <span class="nx">value</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">dispatch</span><span class="p">({</span> <span class="kr">type</span><span class="o">:</span> <span class="s1">&#39;error&#39;</span><span class="p">,</span> <span class="nx">error</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">{</span> <span class="p">...</span><span class="nx">state</span><span class="p">,</span> <span class="nx">run</span> <span class="p">};</span><span class="c1">//返回一个包含`state`( `value`,`error`和`loading`) 和`run`函数的对象。
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">};</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h4 id="2-在组件中使用">2). 在组件中使用</h4>
<ul>
<li><code>component</code></li>
</ul>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">useAsync</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;./customHooks/useAsync&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">RandomImage</span> <span class="o">=</span> <span class="nx">props</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">imgFetch</span> <span class="o">=</span> <span class="nx">useAsync</span><span class="p">(</span><span class="nx">url</span> <span class="o">=&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">fetch</span><span class="p">(</span><span class="nx">url</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="nx">response</span> <span class="o">=&gt;</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">button</span>
</span></span><span class="line"><span class="cl">        <span class="na">onClick</span><span class="o">=</span><span class="p">{()</span> <span class="o">=&gt;</span> <span class="nx">imgFetch</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="s1">&#39;https://dog.ceo/api/breeds/image/random&#39;</span><span class="p">)}</span>
</span></span><span class="line"><span class="cl">        <span class="na">disabled</span><span class="o">=</span><span class="p">{</span><span class="nx">imgFetch</span><span class="p">.</span><span class="nx">isLoading</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">Load</span> <span class="nx">image</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">br</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="nx">imgFetch</span><span class="p">.</span><span class="nx">loading</span> <span class="o">&amp;&amp;</span> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span><span class="nx">Loading</span><span class="p">...&lt;/</span><span class="nt">div</span><span class="p">&gt;}</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="nx">imgFetch</span><span class="p">.</span><span class="nx">error</span> <span class="o">&amp;&amp;</span> <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span><span class="nb">Error</span> <span class="p">{</span><span class="nx">imgFetch</span><span class="p">.</span><span class="nx">error</span><span class="p">}&lt;/</span><span class="nt">div</span><span class="p">&gt;}</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="nx">imgFetch</span><span class="p">.</span><span class="nx">value</span> <span class="o">&amp;&amp;</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">img</span>
</span></span><span class="line"><span class="cl">          <span class="na">src</span><span class="o">=</span><span class="p">{</span><span class="nx">imgFetch</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">message</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">          <span class="na">alt</span><span class="o">=</span><span class="s">&#34;avatar&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">width</span><span class="o">=</span><span class="p">{</span><span class="mi">400</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">          <span class="na">height</span><span class="o">=</span><span class="s">&#34;auto&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">)}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ReactDOM.render(&lt;RandomImage /&gt;, document.getElementById(&#39;root&#39;));
</span></span></span></code></pre></td></tr></table>
</div>
</div><h2 id="四eslint-插件">四、ESLint 插件</h2>
<p>React发布了一个名为 <code>eslint-plugin-react-hooks</code> 的 ESLint 插件来强制执行Hooks规则。</p>
<p>如果项目中没有自动安装，可自行安装配置：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">yarn add --dev eslint-plugin-react-hooks
</code></pre><pre tabindex="0"><code class="language-base" data-lang="base">// 你的 ESLint 配置
{
  &#34;plugins&#34;: [
    // ...
    &#34;react-hooks&#34;
  ],
  &#34;rules&#34;: {
    // ...
    &#34;react-hooks/rules-of-hooks&#34;: &#34;error&#34;, // 检查 Hook 的规则
    &#34;react-hooks/exhaustive-deps&#34;: &#34;warn&#34; // 检查 effect 的依赖
  }
}
</code></pre><h2 id="五hooks-api">五、Hooks API</h2>
<h3 id="1-usestate">1. useState</h3>
<p>返回一个 state，以及更新 state 的函数。</p>
<pre tabindex="0"><code class="language-base" data-lang="base">const [state, setState] = useState(initialState);
</code></pre><p>等号左边名字并不是 React API 的部分，你可以自己取名字。<br />
在初始渲染期间，返回的状态 (state) 与传入的第一个参数 (initialState) 值相同。<br />
这种 JavaScript 语法叫<code>数组解构</code>。等价于</p>
<pre tabindex="0"><code class="language-base" data-lang="base">  var fruitStateVariable = useState(&#39;banana&#39;); // 返回一个有两个元素的数组
  var fruit = fruitStateVariable[0]; // 数组里的第一个值
  var setFruit = fruitStateVariable[1]; // 数组里的第二个值
</code></pre><h4 id="1-普通式更新state">1). 普通式更新state</h4>
<p>在后续的重新渲染中，useState 返回的第一个值将始终是更新后最新的 state。</p>
<pre tabindex="0"><code class="language-base" data-lang="base">setState(newState);
</code></pre><h4 id="2-函数式更新state">2). 函数式更新state</h4>
<p>如果<code>新的state</code>需要通过使用<code>先前的state</code>计算得出，那么可以将<code>函数</code>传递给 <code>setState</code>。该函数将接收先前的 state，并返回一个更新后的值。</p>
<pre tabindex="0"><code class="language-base" data-lang="base">function Counter({initialCount}) {
  const [count, setCount] = useState(initialCount);
  return (
    &lt;&gt;
      Count: {count}
      &lt;button onClick={() =&gt; setCount(initialCount)}&gt;Reset&lt;/button&gt;
      &lt;button onClick={() =&gt; setCount(prevCount =&gt; prevCount - 1)}&gt;-&lt;/button&gt;
      &lt;button onClick={() =&gt; setCount(prevCount =&gt; prevCount + 1)}&gt;+&lt;/button&gt;
    &lt;/&gt;
  );
}
</code></pre><p><code>+</code> 和 <code>-</code> 按钮采用<code>函数式</code>形式，因为被更新的 state 需要基于之前的 state。但是<code>重置</code>按钮则采用<code>普通形式</code>，因为它总是把 count 设置回初始值。</p>
<p>如果你的更新函数返回值与当前 state 完全相同，则随后的重渲染会被完全跳过。</p>
<h4 id="3-合并更新对象">3). 合并更新对象</h4>
<p>与 class 组件中的 setState 方法不同，useState 不会自动合并更新对象。可以用函数式的 setState 结合展开运算符来达到合并更新对象的效果。</p>
<pre tabindex="0"><code class="language-base" data-lang="base">const [state, setState] = useState({});
setState(prevState =&gt; {
  // 也可以使用 Object.assign
  return {...prevState, ...updatedValues};
});
</code></pre><p>useReducer 是另一种可选方案，它更适合用于管理包含多个子值的 state 对象。</p>
<h4 id="4-惰性初始-state">4). 惰性初始 state</h4>
<p>initialState 参数只会在组件的初始渲染中起作用，后续渲染时会被忽略。<br />
如果初始 state 需要通过复杂计算获得，则可以传入一个函数，在函数中计算并返回初始的 state，此函数只在初始渲染时被调用：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">const [state, setState] = useState(() =&gt; {
  const initialState = someExpensiveComputation(props);
  return initialState;
});
</code></pre><h4 id="5-跳过-state-更新">5). 跳过 state 更新</h4>
<p>调用 State Hook 的更新函数<code>setState</code>并传入当前的 state 时，React 将跳过子组件的渲染及 effect 的执行。（React 使用<code>Object.is比较算法</code>来比较 state。）</p>
<p>需要注意的是，React 可能仍需要在跳过渲染前渲染该组件。不过由于 React 不会对组件树的“深层”节点进行不必要的渲染，所以大可不必担心。</p>
<p>如果在渲染期间执行了高开销的计算，则可以使用 useMemo 来进行优化。</p>
<h3 id="2-useeffect">2. useEffect</h3>
<p>Effect Hook 可以让你在函数组件中执行副作用操作(改变 DOM、添加订阅、设置定时器、记录日志等)</p>
<p>赋值给 useEffect 的函数会在组件渲染到屏幕之后执行。</p>
<p>可以把 effect 看作从 React 的纯函数式世界通往命令式世界的逃生通道。</p>
<p>默认情况下，effect 将在每轮渲染结束后执行，但你可以选择让它 在只有某些值改变的时候 才执行。</p>
<pre tabindex="0"><code class="language-base" data-lang="base">useEffect(didUpdate);
</code></pre><h4 id="1-清除-effect">1). 清除 effect</h4>
<p>通常，组件卸载时需要清除 effect 创建的诸如订阅或计时器 ID 等资源。</p>
<p>要实现这一点，useEffect 函数需返回一个清除函数。以下就是一个创建订阅的例子：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">useEffect(() =&gt; {
  const subscription = props.source.subscribe();
  return () =&gt; {
    // 清除订阅
    subscription.unsubscribe();
  };
});
</code></pre><p>为防止内存泄漏，清除函数会在组件卸载前执行。</p>
<p>另外，如果组件多次渲染（通常如此），则在执行下一个 effect 之前，上一个 effect 就已被清除。</p>
<h4 id="2-effect-的执行时机">2). effect 的执行时机</h4>
<p>与 componentDidMount、componentDidUpdate 不同的是，传给 useEffect 的函数会在浏览器完成布局与绘制之后，在一个延迟事件中被调用。<br />
这使得它适用于许多常见的副作用场景，比如设置订阅和事件处理等情况，因为绝大多数操作不应阻塞浏览器对屏幕的更新。</p>
<p>虽然 useEffect 会在浏览器绘制后延迟执行，但会保证在任何新的渲染前执行。<br />
在开始新的更新前，React 总会先清除上一轮渲染的 effect。</p>
<h4 id="3-uselayouteffect">3). useLayoutEffect</h4>
<p>然而，并非所有 effect 都可以被延迟执行。例如，一个对用户可见的 DOM 变更就必须在浏览器执行下一次绘制前被同步执行，这样用户才不会感觉到视觉上的不一致。（概念上类似于被动监听事件和主动监听事件的区别。）React 为此提供了一个额外的 useLayoutEffect Hook 来处理这类 effect。<code>它和 useEffect 的结构相同，区别只是调用时机不同</code>。</p>
<p>它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前，useLayoutEffect 内部的更新计划将被同步刷新。</p>
<p>尽可能使用标准的 useEffect 以避免阻塞视觉更新。</p>
<p>如果你正在将代码从 class 组件迁移到使用 Hook 的函数组件，则需要注意 useLayoutEffect 与 componentDidMount、componentDidUpdate 的调用阶段是一样的。<br />
但是，我们推荐你一开始先用 useEffect，只有当它出问题的时候再尝试使用 useLayoutEffect。</p>
<h4 id="4-ssr-服务器端渲染">4). SSR 服务器端渲染</h4>
<p>如果你使用服务端渲染，请记住，无论 useLayoutEffect 还是 useEffect 都无法在 Javascript 代码加载完成之前执行。<br />
这就是为什么在服务端渲染组件中引入 useLayoutEffect 代码时会触发 React 告警。<br />
解决这个问题，需要将代码逻辑移至 useEffect 中（如果首次渲染不需要这段逻辑的情况下），或是将该组件延迟到客户端渲染完成后再显示（如果直到 useLayoutEffect 执行之前 HTML 都显示错乱的情况下）。</p>
<p>若要从服务端渲染的 HTML 中排除依赖布局 effect 的组件，可以通过使用 <code>showChild &amp;&amp; &lt;Child /&gt;</code> 进行条件渲染，并使用 <code>useEffect(() =&gt; { setShowChild(true); }, [])</code> 延迟展示组件。<br />
这样，在客户端渲染完成之前，UI 就不会像之前那样显示错乱了。</p>
<h4 id="5-effect-的条件执行1">5). effect 的条件执行1</h4>
<p>默认情况下，effect 会在每轮组件渲染完成后执行。这样的话，一旦 effect 的依赖发生变化，它就会被重新创建。</p>
<p>然而，在某些场景下这么做可能会矫枉过正。比如，在上一章节的订阅示例中，我们不需要在每次组件更新时都创建新的订阅，而是仅需要在 source prop 改变时重新创建。</p>
<p>要实现这一点，可以<code>给 useEffect 传递第二个参数</code>，它是 <code>effect 所依赖的值数组</code>。</p>
<p>更新后的示例如下：(此时，只有当 props.source 改变后才会重新创建订阅。)</p>
<pre tabindex="0"><code class="language-base" data-lang="base">useEffect(
  () =&gt; {
    const subscription = props.source.subscribe();
    return () =&gt; {
      subscription.unsubscribe();
    };
  },
  [props.source],
);
</code></pre><h4 id="5-effect-的条件执行2">5). effect 的条件执行2</h4>
<p>如果你要使用此优化方式，请确保数组中包含了所有外部作用域中会发生变化且在 effect 中使用的变量，否则你的代码会引用到先前渲染中的旧变量。</p>
<p>如果想执行只运行一次的 effect（仅在组件挂载和卸载时执行），可以传递一个空数组（<code>[]</code>）作为第二个参数。<br />
这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值，所以它永远都不需要重复执行。这并不属于特殊情况 —— 它依然遵循输入数组的工作方式。</p>
<p>如果你传入了一个空数组（<code>[]</code>），effect 内部的 props 和 state 就会一直持有其<code>初始值</code>。<br />
尽管传入 [] 作为第二个参数有点类似于 componentDidMount 和 componentWillUnmount 的思维模式，但它是更好的方式来避免过于频繁的重复调用 effect。</p>
<p>除此之外，请记得 React 会等待浏览器完成画面渲染之后才会延迟调用 useEffect，因此会使得处理额外操作很方便。</p>
<p>React推荐启用 eslint-plugin-react-hooks 中的 <code>exhaustive-deps</code> 规则。此规则会在添加错误依赖时发出警告并给出修复建议。</p>
<p>依赖项数组不会作为参数传给 effect 函数。虽然从概念上来说它表现为：所有 effect 函数中引用的值都应该出现在依赖项数组中。未来编译器会更加智能，届时自动创建数组将成为可能。</p>
<h3 id="3-usecontext">3. useContext</h3>
<h6 id="我个人绝得usecontext-只适合用来做ui主题的传递">我个人绝得，useContext 只适合用来做UI主题的传递</h6>
<pre tabindex="0"><code class="language-base" data-lang="base">const value = useContext(MyContext);
</code></pre><p>接收一个 context 对象（React.createContext 的返回值）并返回该 context 的当前值。<br />
当前的 context 值由上层组件中距离当前组件最近的<code>&lt;MyContext.Provider&gt;</code>的<code>value prop</code>决定。</p>
<p>当组件上层最近的<code>&lt;MyContext.Provider&gt;</code>更新时，该 Hook 会触发重渲染，并使用最新传递给 <code>MyContext provider</code> 的 <code>context value</code> 值。</p>
<h6 id="即使祖先使用-reactmemo-或-shouldcomponentupdate也会在组件本身使用-usecontext-时重新渲染">即使祖先使用 <code>React.memo</code> 或 <code>shouldComponentUpdate</code>，也会在组件本身使用 useContext 时重新渲染。</h6>
<p>别忘记 useContext 的参数必须是 context 对象本身：</p>
<p>正确： useContext(MyContext)<br />
错误： useContext(MyContext.Consumer)<br />
错误： useContext(MyContext.Provider)</p>
<p>调用了 useContext 的组件总会在 context 值变化时重新渲染。如果重渲染组件的开销较大，你可以 通过使用 <code>memoization</code> 来优化。</p>
<h4 id="使用方法">使用方法</h4>
<p>useContext(MyContext) 只是让你能够读取 context 的值以及订阅 context 的变化。你仍然需要在上层组件树中使用 <code>&lt;MyContext.Provider&gt;</code> 来为下层组件提供 <code>context</code>。</p>
<pre tabindex="0"><code class="language-base" data-lang="base">const themes = {
  light: {
    foreground: &#34;#000000&#34;,
    background: &#34;#eeeeee&#34;
  },
  dark: {
    foreground: &#34;#ffffff&#34;,
    background: &#34;#222222&#34;
  }
};
------------------------Step1. 创建上下文----------------
const ThemeContext = React.createContext(themes.light);
--------------------------------------------------------
function App() {
  return (
------------------------Step2. 提供上下文----------------
    &lt;ThemeContext.Provider value={themes.dark}&gt;
      &lt;Toolbar /&gt;
    &lt;/ThemeContext.Provider&gt;
--------------------------------------------------------
  );
}

function Toolbar(props) {
  return (
    &lt;div&gt;
      &lt;ThemedButton /&gt;
    &lt;/div&gt;
  );
}

function ThemedButton() {
------------------------Step3. 使用上下文---------------------
  const theme = useContext(ThemeContext);
  return (
    &lt;button style={{ background: theme.background, color: theme.foreground }}&gt;
      I am styled by theme context!
    &lt;/button&gt;
  );
--------------------------------------------------------------
}
</code></pre><h3 id="4-usereducer">4. useReducer</h3>
<pre tabindex="0"><code class="language-base" data-lang="base">const [state, dispatch] = useReducer(reducer, initialArg, init);
</code></pre><p>useState 的替代方案。它接收一个形如 <code>(state, action) =&gt; newState</code> 的 reducer，并<code>返回当前的 state 以及与其配套的 dispatch 方法</code>。（如果你熟悉 Redux 的话，就已经知道它如何工作了。）</p>
<p>在某些场景下，useReducer 会比 useState 更适用，例如 state 逻辑较复杂且包含多个子值，或者下一个 state 依赖于之前的 state 等。<br />
并且，使用 useReducer 还能给那些会触发深更新的组件做性能优化，因为<code>可以向子组件传递 dispatch 而不是回调函数</code>。</p>
<h4 id="1-使用方法">1). 使用方法</h4>
<p>以下是用 reducer 重写 useState 一节的计数器示例：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case &#39;increment&#39;:
      return {count: state.count + 1};
    case &#39;decrement&#39;:
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {

  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    &lt;&gt;
      Count: {state.count}
      &lt;button onClick={() =&gt; dispatch({type: &#39;decrement&#39;})}&gt;-&lt;/button&gt;
      &lt;button onClick={() =&gt; dispatch({type: &#39;increment&#39;})}&gt;+&lt;/button&gt;
    &lt;/&gt;
  );
}
</code></pre><h4 id="2-指定初始-state">2). 指定初始 state</h4>
<p>有两种不同初始化 useReducer state 的方式，可以根据使用场景选择其中的一种。<br />
将初始 state 作为第二个参数传入 useReducer 是最简单的方法：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">const [state, dispatch] = useReducer(
    reducer,
    {count: initialCount}
  );
</code></pre><p>React 不使用 state = initialState 这一由 Redux 推广开来的参数约定。<br />
有时候初始值依赖于 props，因此需要在调用 Hook 时指定。<br />
如果特别喜欢Redux的参数约定，可以通过调用 useReducer(reducer, undefined, reducer) 来模拟 Redux 的行为，但React不鼓励这么做。</p>
<h4 id="3-惰性初始化">3). 惰性初始化</h4>
<p>可以选择惰性地创建初始 state。为此，需要将 <code>init 函数</code>作为 useReducer 的<code>第三个参数</code>传入，这样初始 state 将被设置为 <code>init(initialArg)</code>。</p>
<p>这么做可以将用于计算 state 的逻辑提取到 reducer 外部，这也为将来对<code>重置 state</code> 的 action 做处理提供了便利：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">function init(initialCount) {
  return {count: initialCount};
}

function reducer(state, action) {
  switch (action.type) {
    case &#39;increment&#39;:
      return {count: state.count + 1};
    case &#39;decrement&#39;:
      return {count: state.count - 1};
    case &#39;reset&#39;:
      return init(action.payload);
    default:
      throw new Error();
  }
}

function Counter({initialCount}) {
  const [state, dispatch] = useReducer(reducer, initialCount, init);
  return (
    &lt;&gt;
      Count: {state.count}
      &lt;button
        onClick={() =&gt; dispatch({type: &#39;reset&#39;, payload: initialCount})}&gt;
        Reset
      &lt;/button&gt;
      &lt;button onClick={() =&gt; dispatch({type: &#39;decrement&#39;})}&gt;-&lt;/button&gt;
      &lt;button onClick={() =&gt; dispatch({type: &#39;increment&#39;})}&gt;+&lt;/button&gt;
    &lt;/&gt;
  );
}
</code></pre><h4 id="4-跳过-dispatch">4). 跳过 dispatch</h4>
<p>如果 Reducer Hook 的返回值与当前 state 相同，React 将跳过子组件的渲染及副作用的执行。（React 使用 <code>Object.is 比较算法</code> 来比较 state。）</p>
<p>需要注意的是，React 可能仍需要在跳过渲染前再次渲染该组件。不过由于 React 不会对组件树的“深层”节点进行不必要的渲染，所以大可不必担心。<br />
如果你在渲染期间执行了高开销的计算，则可以使用 useMemo 来进行优化。</p>
<h3 id="5-usecallback">5. useCallback</h3>
<pre tabindex="0"><code class="language-base" data-lang="base">const memoizedCallback = useCallback(
  () =&gt; {
    doSomething(a, b);
  },
  [a, b],
);
</code></pre><p>返回一个 <code>memoized</code> 回调函数。</p>
<p>Memoization 在函数式编程语言的编译器中大量使用，这些语言通常使用按名称调用评估策略。<br />
为了避免计算参数值的开销，这些语言的编译器大量使用称为thunk 的辅助函数来计算参数值，并记住这些函数以<code>避免重复计算</code>。</p>
<p>把<code>内联回调函数</code>及依赖项<code>数组</code>作为参数传入 <code>useCallback</code>，它将返回该回调函数的 <code>memoized</code> 版本，该回调函数<code>仅在某个依赖项改变时才会更新</code>。</p>
<h6 id="当把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染例如-shouldcomponentupdate的子组件时它将非常有用">当把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染（例如 shouldComponentUpdate）的子组件时，它将非常有用。</h6>
<p><code>useCallback(fn, deps)</code> 相当于 <code>useMemo(() =&gt; fn, deps)</code>。</p>
<p>依赖项数组不会作为参数传给回调函数。虽然从概念上来说它表现为：所有回调函数中引用的值都应该出现在依赖项数组中。未来编译器会更加智能，届时自动创建数组将成为可能。</p>
<p>React推荐启用 eslint-plugin-react-hooks 中的 <code>exhaustive-deps</code> 规则。此规则会在添加错误依赖时发出警告并给出修复建议。</p>
<h4 id="1-usecallback的目的">1. useCallback()的目的</h4>
<p>React 组件中通常会创建<code>共享相同代码</code>的<code>不同函数对象</code>：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">MyComponent</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// handleClick 在每次渲染时重新创建
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">const</span> <span class="nx">handleClick</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Clicked!&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>handleClick</code>是<code>MyComponent</code>每次渲染的不同函数对象。</p>
<p>因为内联函数很便宜，所以在每次渲染时重新创建函数不是问题。每个组件有几个内联函数是可以接受的。</p>
<p>但在某些情况下，需要在渲染之间维护<code>单个函数实例</code>：</p>
<ol>
<li>函数组件里面包裹一个接受函数对象的props的<code>React.memo()</code></li>
<li>当函数对象依赖于其他钩子时，例如 <code>useEffect(..., [callback])</code></li>
<li>当函数有一些内部状态时，例如去抖动函数<br />
（基本上去抖动确保为可能发生多次的事件发送恰好一个信号。<br />
节流将函数接收的调用频率限制在一个固定的时间间隔内。它用于确保目标函数的调用频率不会超过指定的延迟。）。</li>
</ol>
<p>这时，<code>useCallback(callbackFun, deps)</code> 就派上用场了：给定相同的<code>依赖值deps</code>，钩子在渲染之间<code>返回相同的函数实例</code>（又名记忆）：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">useCallback</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;react&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">MyComponent</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// handleClick is the same function object
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">const</span> <span class="nx">handleClick</span> <span class="o">=</span> <span class="nx">useCallback</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Clicked!&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span> <span class="p">[]);</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>handleClick 变量在 MyComponent 的渲染之间始终具有相同的回调函数对象。</p>
<h4 id="2-usecallback的正确用例">2. useCallback的正确用例</h4>
<p>想象一下，有一个呈现大量数据列表的组件：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">import useSearch from &#39;./fetch-items&#39;;
function MyBigList({ term, onItemClick }) {
  const items = useSearch(term);
  const map = item =&gt; &lt;div onClick={onItemClick}&gt;{item}&lt;/div&gt;;
  return &lt;div&gt;{items.map(map)}&lt;/div&gt;;
}
export default React.memo(MyBigList);
</code></pre><p>列表可能很大，可能有数百个项目。为了防止无用的列表重新呈现，将其包装到<code>React.memo()</code>.</p>
<p>MyBigList 的父组件提供了一个知道何时单击了一个项目的处理函数：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">import { useCallback } from &#39;react&#39;;
export function MyParent({ term }) {
--------------------------------------------------------
  const onItemClick = useCallback(event =&gt; {
    console.log(&#39;You clicked &#39;, event.currentTarget);
  }, [term]);
--------------------------------------------------------
  return (
    &lt;MyBigList
      term={term}
      onItemClick={onItemClick}
    /&gt;
  );
}
</code></pre><p>onItemClick回调由useCallback()处理. 只要term是相同的，useCallback()就返回相同的函数对象。</p>
<p>当MyParent组件重新渲染时，onItemClick函数对象保持不变并且不会破坏MyBigList.</p>
<p>这就是useCallback()的目的和正确用法。</p>
<h4 id="3-usecallback的糟糕用例">3. useCallback的糟糕用例</h4>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">useCallback</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;react&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">MyComponent</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// `useCallback()` 被无脑地使用,在这里毫无意义
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">const</span> <span class="nx">handleClick</span> <span class="o">=</span> <span class="nx">useCallback</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 处理点击事件
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="p">},</span> <span class="p">[]);</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="o">&lt;</span><span class="nx">MyChild</span> <span class="nx">onClick</span><span class="o">=</span><span class="p">{</span><span class="nx">handleClick</span><span class="p">}</span> <span class="o">/&gt;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">MyChild</span> <span class="p">({</span> <span class="nx">onClick</span> <span class="p">})</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="o">&lt;</span><span class="nx">button</span> <span class="nx">onClick</span><span class="o">=</span><span class="p">{</span><span class="nx">onClick</span><span class="p">}</span><span class="o">&gt;</span><span class="nx">I</span> <span class="nx">am</span> <span class="nx">a</span> <span class="nx">child</span><span class="o">&lt;</span><span class="err">/button&gt;;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>通过使用useCallback()增加了代码复杂性。必须使<code>depsofuseCallback(..., deps)</code>与你在<code>记忆化回调</code>中使用的内容保持同步。</p>
<p>总之，优化比没有优化的成本更高。</p>
<p>在这里，完全可以如此处理：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">import { useCallback } from &#39;react&#39;;
function MyComponent() {
  const handleClick = () =&gt; {
    // handle the click event
  };
  return &lt;MyChild onClick={handleClick} /&gt;;
}
function MyChild ({ onClick }) {
  return &lt;button onClick={onClick}&gt;I am a child&lt;/button&gt;;
}
</code></pre><h4 id="4-总结所有性能优化的建议">4). 总结（所有性能优化的建议）</h4>
<p>在考虑性能调整时，请记住以下语句：</p>
<ul>
<li>在优化之前先分析</li>
</ul>
<p>在决定使用优化技术时，包括<code>记忆化</code>，特别是<code>useCallback()</code>，请执行以下操作：</p>
<ul>
<li>第一 分析</li>
<li>然后量化增加的性能（例如150ms与50ms渲染速度增加）</li>
</ul>
<p>然后问问自己：与增加的复杂性相比，增加的性能值得使用 useCallback() 吗？</p>
<h3 id="6-usememo">6. useMemo</h3>
<p>可以把 useMemo 作为性能优化的手段，但不要把它当成语义上的保证。<br />
将来，React 可能会选择“遗忘”以前的一些 memoized 值，并在下次渲染时重新计算它们，比如为离屏组件释放内存。<br />
先编写在没有 useMemo 的情况下也可以执行的代码 —— 之后再在代码中添加 useMemo，以达到优化性能的目的。</p>
<pre tabindex="0"><code class="language-base" data-lang="base">const memoizedValue = useMemo(() =&gt; computeExpensiveValue(a, b), [a, b]);
</code></pre><p>在初始渲染期间，<code>useMemo(compute, dependencies)</code>调用<code>compute</code>，记忆计​​算结果，并将其返回给组件。</p>
<p>如果在下一次渲染期间<code>依赖项没有改变</code>，则 useMemo() 不会调用 compute 但返回<code>记忆值</code>。</p>
<p>但是如果在重新渲染期间<code>依赖项发生变化</code>，则 useMemo() 调用 compute，<code>记忆新值</code>并返回它。</p>
<p>这就是useMemo()钩子的本质。</p>
<p>如果你的计算回调使用<code>props</code>或<code>state</code>值，请确保将这些值指示为<code>依赖项</code>：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">const memoizedResult = useMemo(() =&gt; {
  return expensiveFunction(propA, propB);
}, [propA, propB]);
</code></pre><h4 id="1-usememo-一个例子">1). useMemo() 一个例子</h4>
<p>组件<code>&lt;CalculateFactorial /&gt;</code>计算输入字段的数字的阶乘。</p>
<img src='https://blogger.googleusercontent.com/img/a/AVvXsEjfi0tvgpm4OW2uDPODyMYI_xQrsUsNm7pDAWYl7_-4F13gycHG7PRAF3eEJ0K--2UkF6XRa6TPF6G5sIf4GII_gxzoXSWdorDLhGiHBYtinu6imYNkUPuecfmAYx5TCPXIXx7nuaV-K4zu9vsRX3ig8s613BfbFPR0-OL76I8ReHs18D1vv2KD9TMrmA=w597-h238'/>
<p>这是<code>&lt;CalculateFactorial /&gt;</code>组件的可能实现：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">import { useState } from &#39;react&#39;;
export function CalculateFactorial() {
  const [number, setNumber] = useState(1);
  const [inc, setInc] = useState(0);
  const factorial = factorialOf(number);
  const onChange = event =&gt; {
    setNumber(Number(event.target.value));
  };
  const onClick = () =&gt; setInc(i =&gt; i + 1);
  
  return (
    &lt;div&gt;
      Factorial of 
      &lt;input type=&#34;number&#34; value={number} onChange={onChange} /&gt;
      is {factorial}
      &lt;button onClick={onClick}&gt;Re-render&lt;/button&gt;
    &lt;/div&gt;
  );
}
function factorialOf(n) {
  console.log(&#39;factorialOf(n) called!&#39;);
  return n &lt;= 0 ? 1 : n * factorialOf(n - 1);
}
</code></pre><img src='https://blogger.googleusercontent.com/img/a/AVvXsEjzZttXzxbVPWgIcWt-CTE91d_3qcQ2NuMxEBkEiH7gc0473134TZ-3IRfw8axZVqdhwjKhxhl8qCQc345bkGyERtp-_Txl_8KA7VW6_KIcjr5h0NOQCe4c1RcBjE0JlYhl3QLVAMjRdsE73qehFeeVmxWO2NewD8sQ-Zgkf_olAD23KuxEhrKTX7FCug=w455-h479'>
<p>每次更改输入值时，都会计算阶乘factorialOf(n)并&rsquo;factorialOf(n) called!&lsquo;记录到控制台。</p>
<p>另一方面，每次单击重新渲染按钮时，<code>inc</code>状态值都会更新。更新<code>in</code>c状态值会触发<code>&lt;CalculateFactorial /&gt;</code>重新渲染。<br />
但是，作为次要效果，在重新渲染期间，阶乘会再次重新计算 -&lsquo;factorialOf(n) called!&lsquo;记录到控制台。</p>
<p>当组件重新渲染时，如何记住阶乘计算？这时候就是使用 useMemo() 的时候了。</p>
<p>通过使用<code>useMemo(() =&gt; factorialOf(number), [number])</code>而不是普通的 <code>factorialOf(number)</code>。React 记住阶乘计算。</p>
<p>改进<code>&lt;CalculateFactorial /&gt;</code>并记住阶乘计算：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">import { useState, useMemo } from &#39;react&#39;;
export function CalculateFactorial() {
  const [number, setNumber] = useState(1);
  const [inc, setInc] = useState(0);
-----------------------------------------------------------------------
  const factorial = useMemo(() =&gt; factorialOf(number), [number]);
-----------------------------------------------------------------------
  const onChange = event =&gt; {
    setNumber(Number(event.target.value));
  };
  const onClick = () =&gt; setInc(i =&gt; i + 1);
  
  return (
    &lt;div&gt;
      Factorial of 
      &lt;input type=&#34;number&#34; value={number} onChange={onChange} /&gt;
      is {factorial}
      &lt;button onClick={onClick}&gt;Re-render&lt;/button&gt;
    &lt;/div&gt;
  );
}
function factorialOf(n) {
  console.log(&#39;factorialOf(n) called!&#39;);
  return n &lt;= 0 ? 1 : n * factorialOf(n - 1);
}
</code></pre><img src='https://blogger.googleusercontent.com/img/a/AVvXsEibvnHaubP3KGm0O9ygOP6eN_GRcCOE6Dcfx8iIyFlvkWmpoSvbAbGoV4r85dZhh-ZnziOB3I8E4o94FatzhQJ3nsAT2kENZ5nBo1E57fQnOhfl9p-Vj71NeHrLr4bS1AaGkcCYc7_2iD3va22FAGJSma0xQxcqOyaDWDWcTAKgwY2qNnOqqe-Na-kEpA=w456-h463' />
<p>每次更改数字的值时，&lsquo;factorialOf(n) called!&lsquo;都会记录到控制台。这是预期的。</p>
<p>但是，如果您<code>单击重新渲染按钮</code>，&lsquo;factorialOf(n) called!&lsquo;则不会记录到控制台，因为<code>useMemo(() =&gt; factorialOf(number), [number])</code>返回记忆化的阶乘计算。很棒！</p>
<h4 id="2-usememo与usecallback">2). useMemo()与useCallback()</h4>
<p>useCallback() 与 useMemo() 相比，是一个更专业的钩子，可以记住回调：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">import { useCallback } from &#39;react&#39;;
function MyComponent({ prop }) {
  const callback = () =&gt; {
    return &#39;Result&#39;;
  };
  const memoizedCallback = useCallback(callback, [prop]);
  
  return &lt;ChildComponent callback={memoizedCallback} /&gt;;
}
</code></pre><p>在上面的示例中，<code>useCallback(() =&gt; {...}, [prop])</code>只要<code>prop依赖项相同</code>，就返回<code>相同的函数实例</code>。</p>
<p>可以使用<code>useMemo()</code>做相同的方式来记忆回调：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">import { useMemo } from &#39;react&#39;;
function MyComponent({ prop }) {
  const callback = () =&gt; {
    return &#39;Result&#39;;
  };
  const memoizedCallback = useMemo(() =&gt; callback, [prop]);
  
  return &lt;ChildComponent callback={memoizedCallback} /&gt;;
}
</code></pre><h4 id="3-小心使用记忆memoization">3). 小心使用记忆(memoization)</h4>
<p>虽然useMemo()可以提高组件的性能，但必须确保使用和不使用挂钩来配置组件。只有在那之后才能得出是否值得记忆的结论。</p>
<p>当记忆使用不当时，可能会损害性能。</p>
<h4 id="4-结语">4). 结语</h4>
<p><code>useMemo(() =&gt; computation(a, b), [a, b])</code>是记住昂贵计算的钩子。<br />
给定相同的<code>[a, b]</code>依赖项，一旦记忆，钩子将返回<code>记忆值</code>而不调用<code>computation(a, b)</code></p>
<h3 id="7-useref">7. useRef</h3>
<p>React.useRef()钩子创建持久化的可变值（也称为 ref/reference ），以及访问 DOM 元素。</p>
<h4 id="1-可变值">1). 可变值</h4>
<p>useRef(initialValue)是一个内置的 React 钩子，它<code>接受一个参数作为初始值</code>并<code>返回</code>一个<code>引用（又名ref）</code>。<code>引用是具有特殊属性</code>的对象<code>current</code>。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">useRef</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;react&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">MyComponent</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">reference</span> <span class="o">=</span> <span class="nx">useRef</span><span class="p">(</span><span class="nx">initialValue</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">someHandler</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Access reference value:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kr">const</span> <span class="nx">value</span> <span class="o">=</span> <span class="nx">reference</span><span class="p">.</span><span class="nx">current</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Update reference value:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">reference</span><span class="p">.</span><span class="nx">current</span> <span class="o">=</span> <span class="nx">newValue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ...
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>reference.current <code>访问</code> ref 值，reference.current = newValue <code>更新</code> ref 值。很简单。</p>
<h4 id="用例记录按钮点击">用例：记录按钮点击</h4>
<p>该组件<code>LogButtonClicks</code>使用<code>ref</code>来<code>存储</code>按钮的点击次数：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">import { useRef } from &#39;react&#39;;
function LogButtonClicks() {
  const countRef = useRef(0);
  
  const handle = () =&gt; {
    countRef.current++;
    console.log(`Clicked ${countRef.current} times`);
  };
  console.log(&#39;I rendered!&#39;);
  return &lt;button onClick={handle}&gt;Click me&lt;/button&gt;;
}
</code></pre><img src='https://blogger.googleusercontent.com/img/a/AVvXsEgN_UoUOCiAzBlaRcb1dyZtCLLTDMgttHwV81w9dFwSg-o850WPyzA_x2N_OwYkeBO-9BXcKJ6mtJKMO0l8exwEs_eAaw4kOcJnsEUfAj2C8Uz4SQQE5V3SBYn64VrwnZET4stixa9AC4qrtjYgT6TqW1fAJcmRZk0NKPB7aLFBIjlFLR5GPYRMoImmLQ=w439-h465'/>
<p><code>const countRef = useRef(0)</code>创建一个用<code>countRef</code>初始化的ref<code>0</code>。</p>
<p>单击按钮时，handle将调用函数并<code>递增ref值</code>：<code>countRef.current++</code>。ref值记录到控制台。</p>
<p>更新ref值<code>countRef.current++</code>不会触发组件重新渲染。这<code>可以通过'I rendered!'在初始渲染时仅记录到控制台一次的事实来证明</code>，并且在更新ref时不会发生重新渲染。</p>
<p>现在提出一个合理的问题：<code>ref</code>和<code>state</code>之间的<code>主要区别</code>是什么？</p>
<h4 id="11-ref-和-state-的区别">1.1). ref 和 state 的区别</h4>
<p>重用<code>LogButtonClicks</code>组件，但这次使用<code>useState()</code>钩子来计算按钮点击次数：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">import { useState } from &#39;react&#39;;
function LogButtonClicks() {
  const [count, setCount] = useState(0);
  
  const handle = () =&gt; {
    const updatedCount = count + 1;
    console.log(`Clicked ${updatedCount} times`);
    setCount(updatedCount);
  };
  console.log(&#39;I rendered!&#39;);
  return &lt;button onClick={handle}&gt;Click me&lt;/button&gt;;
}
</code></pre><img src='https://blogger.googleusercontent.com/img/a/AVvXsEgbU1ehj8EvlcYbO0Y2j3Tk7RuKaLVP5ypo4qoYLOtgzlqGvUzafUyQEbLWKT2PSfIaNz64eC2HYXvRyi_sVXkGPXf3ReUN0cprJN6byRakhXdgOy-wtqnt3ZeykHQe87DTN-WF54T1k4MYxijCR16YKtw9ZFh0HxDncKcoz8o4YVOqfrhAHhiiUZU4kw=w433-h600' />
<p>每次单击时，都会在控制台中看到消息&rsquo;I rendered!&rsquo;—— <code>这意味着每次更新状态时，组件都会重新渲染</code>。</p>
<p>因此，引用和状态之间的两个主要区别：</p>
<ol>
<li>更新ref不会触发重新渲染，而更新state会使组件重新渲染；</li>
<li>ref更新是<code>同步</code>的（更新后的ref值立即可用），而state更新是<code>异步</code>的（重新渲染后更新状态变量）。</li>
</ol>
<p>从更高的角度来看，ref<code>存储</code>副作用的基础结构数据，而state<code>存储</code>直接呈现在屏幕上的信息。</p>
<h4 id="用例实现秒表">用例：实现秒表</h4>
<p>可以将副作用的ref基础结构数据存储在内部。例如，可以存储到ref指针中：计时器 ID、套接字 ID 等。</p>
<p>该组件<code>Stopwatch</code>使用<code>setInterval(callback, time)</code>计时器功能每秒增加秒数的计数器。计时器 id 存储到ref中<code>timerIdRef</code>：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">import { useRef, useState, useEffect } from &#39;react&#39;;
function Stopwatch() {
  const timerIdRef = useRef(0);
  const [count, setCount] = useState(0);
  const startHandler = () =&gt; {
    if (timerIdRef.current) { return; }
    timerIdRef.current = setInterval(() =&gt; setCount(c =&gt; c+1), 1000);
  };
  const stopHandler = () =&gt; {
    clearInterval(timerIdRef.current);
    timerIdRef.current = 0;
  };
  useEffect(() =&gt; {
    return () =&gt; clearInterval(timerIdRef.current);
  }, []);
  return (
    &lt;div&gt;
      &lt;div&gt;Timer: {count}s&lt;/div&gt;
      &lt;div&gt;
        &lt;button onClick={startHandler}&gt;Start&lt;/button&gt;
        &lt;button onClick={stopHandler}&gt;Stop&lt;/button&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}
</code></pre><img src='https://blogger.googleusercontent.com/img/a/AVvXsEhYFdjtCeZZUhFT-Sgx-kPtcctBJ9JfEDwwP4Kjgfm9xTArh4MtT52-2JGAHRpQHu463gTxG_GSSEMBJNChjRSngGql17OgXmqyg95RcjKkzfoFCIKX9p0zQC5U74A8HOmroUS-im7pS2QwNHc53oIFN9nkiZItPrbc--Gr0hAp2Bzl0Y2ZPZHkl2DM1w=w473-h325'/>
<p><code>startHandler()单击Start按钮时</code>调用<code>timerIdRef.current = setInterval(...)</code>函数<code>启动计时器并将计时器 id 保存在ref中</code>。</p>
<p>用户单击停止按钮停止秒表。<code>停止按钮处理程序stopHandler()</code>从ref访问计时器ID，并停止<code>定时器clearInterval(timerIdRef.current)</code>。</p>
<p>此外，如果组件在秒表处于活动状态的情况下卸载，则<code>useEffect()</code>的清理功能也将停止计时器。</p>
<p>在秒表示例中，ref用于存储基础设施数据 — <code>活动计时器 ID</code>。</p>
<p>附带挑战：通过添加重置按钮来改进秒表</p>
<pre tabindex="0"><code class="language-base" data-lang="base">//待编写
</code></pre><h4 id="2-访问-dom-元素">2). 访问 DOM 元素</h4>
<p>useRef()钩子的另一个有用的应用是访问 DOM 元素。这分 3 个步骤执行：</p>
<ol>
<li>定义访问元素的ref <code>const elementRef = useRef()</code>；</li>
<li>将ref分配给<code>ref元素的属性</code>：<code>&lt;div ref={elementRef}&gt;&lt;/div&gt;</code>;</li>
<li>挂载后，<code>elementRef.current</code>指向DOM元素。</li>
</ol>
<pre tabindex="0"><code class="language-base" data-lang="base">import { useRef, useEffect } from &#39;react&#39;;
function AccessingElement() {
  const elementRef = useRef();
   useEffect(() =&gt; {
    const divElement = elementRef.current;
    console.log(divElement); // logs &lt;div&gt;I&#39;m an element&lt;/div&gt;
  }, []);
  return (
    &lt;div ref={elementRef}&gt;
      I&#39;m an element
    &lt;/div&gt;
  );
}
</code></pre><img src='https://blogger.googleusercontent.com/img/a/AVvXsEgBwbd5nTJtZgYI9X_D0u4eHnlnMWgLF3L3bjM-sxjEBtKgj2F20rdrGnxbA20zjxznpo6S0vbJwt7piqFHCwY00R75jm48ND9yLd7j0QwuMd5pr1nPZA6OIvkfBYAWSXPnao0CwJtr57ikk0WTTDmnQk2EcSINE2hEPp0_rhJQVZSh9voxQv0j1wiYvg=w468-h275'/>
<h4 id="用例聚焦输入">用例：聚焦输入</h4>
<p>访问 DOM 元素，以便在组件挂载时关注输入字段。</p>
<p>要使其工作，需要创建对输入的ref，将ref分配给标签的ref属性，并在安装后调用<code>element.focus()</code>元素上的特殊方法。</p>
<p>这是该<code>&lt;InputFocus&gt;</code>组件的可能实现：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">import { useRef, useEffect } from &#39;react&#39;;
function InputFocus() {
  const inputRef = useRef();
  useEffect(() =&gt; {
    inputRef.current.focus();
  }, []);
  return (
    &lt;input 
      ref={inputRef} 
      type=&#34;text&#34; 
    /&gt;
  );
}
</code></pre><p>const inputRef = useRef() 创建一个ref来保存输入元素。</p>
<p>然后inputRef分配给输入字段的属性ref：<code>&lt;input ref={inputRef} type=&quot;text&quot; /&gt;</code>。</p>
<p>然后，在安装后，设置<code>inputRef.current</code>为输入元素。现在，可以通过编程将焦点设置输入到：<code>inputRef.current.focus()</code>。</p>
<h6 id="ref-在初始渲染时为空">Ref 在初始渲染时为空</h6>
<p>在初始渲染期间，保存 DOM 元素的ref应该是空的：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">import { useRef, useEffect } from &#39;react&#39;;
function InputFocus() {
  const inputRef = useRef();
  useEffect(() =&gt; {
    // Logs `HTMLInputElement` 
    console.log(inputRef.current);
    inputRef.current.focus();
  }, []);
  // Logs `undefined` during initial rendering
  console.log(inputRef.current);
  return &lt;input ref={inputRef} type=&#34;text&#34; /&gt;;
}
</code></pre><img src='https://blogger.googleusercontent.com/img/a/AVvXsEijKOFIUWDaiOvqLpS5YaW3urcc08nIosTX3ueI_u-xWfCAV4mhSlCmTwTd8t9-YWttK7UwGUM06mDovcZ3jYWylgeAi3OP4fOEKujQk4PEoonkryXbSx_TrTcmr3s6HPsANiashmnCCQajJUNgjm2RTLCIM02fixVjqacLCdB2iq6cKWH04TXr__Ff3Q=w481-h404'/>
<p>在初始渲染期间，React 仍不确定组件的输出是什么，因此还没有创建 DOM 结构。这就是为什么在初始渲染期间inputRef.current评估为undefined。</p>
<p>useEffect(callback, []) 当输入元素已经在 DOM 中创建时，钩子在挂载后立即调用回调。</p>
<p>callback的函数useEffect(callback, [])使inputRef.current访问的正确位置，因为它可以保证构建 DOM。</p>
<h4 id="3-更新ref限制">3). 更新ref限制</h4>
<p>功能组件的功能范围应该计算输出或调用钩子。</p>
<p>这就是为什么不应该在组件功能的直接范围内执行更新ref（以及更新state）的原因。</p>
<p>必须在useEffect()回调或处理程序（事件处理程序、计时器处理程序等）内部更新ref。</p>
<pre tabindex="0"><code class="language-base" data-lang="base">import { useRef, useEffect } from &#39;react&#39;;
function MyComponent({ prop }) {
  const myRef = useRef(0);
  useEffect(() =&gt; {
    myRef.current++; // Good!
    setTimeout(() =&gt; {
      myRef.current++; // Good!
    }, 1000);
  }, []);
  const handler = () =&gt; {
    myRef.current++; // Good!
  };
  myRef.current++; // Bad!
  if (prop) {
    myRef.current++; // Bad!
  }
  return &lt;button onClick={handler}&gt;My button&lt;/button&gt;;
}
</code></pre><h4 id="4-总结">4). 总结</h4>
<p>useRef() 钩子创建引用。</p>
<p><code>const reference = useRef(initialValue)</code>使用初始值调用会返回一个名为 <code>reference</code> 的特殊对象。<br />
ref对象有一个属性<code>current</code>：你可以使用这个属性来读取ref值<code>reference.current</code>，或者更新<code>reference.current = newValue</code>。</p>
<p>在组件重新渲染之间，引用的值是持久的。</p>
<p>与更新状态相反，更新引用不会触发组件重新渲染。</p>
<p>引用也可以访问 DOM 元素。将引用分配给ref您要访问的元素的属性：<code>&lt;div ref={reference}&gt;Element&lt;/div&gt;</code>— 该元素位于<code>reference.current</code>。</p>
<h3 id="8-useimperativehandle">8. useImperativeHandle</h3>
<pre tabindex="0"><code class="language-base" data-lang="base">useImperativeHandle(ref, createHandle, [deps])
</code></pre><p>useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值。在大多数情况下，应当避免使用 ref 这样的命令式代码。</p>
<p>useImperativeHandle 应当与 <code>forwardRef</code> 一起使用：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">function FancyInput(props, ref) {
  const inputRef = useRef();
  useImperativeHandle(ref, () =&gt; ({
    focus: () =&gt; {
      inputRef.current.focus();
    }
  }));
  return &lt;input ref={inputRef} ... /&gt;;
}
FancyInput = forwardRef(FancyInput);
</code></pre><p>在本例中，渲染 <code>&lt;FancyInput ref={inputRef} /&gt;</code> 的父组件可以调用 <code>inputRef.current.focus()</code>。</p>
<h3 id="9-usedebugvalue">9. useDebugValue</h3>
<pre tabindex="0"><code class="language-base" data-lang="base">useDebugValue(value)
</code></pre><p>useDebugValue 可用于在 React 开发者工具中显示自定义 hook 的标签。</p>
<p>例如，<code>自定义 Hook</code> 章节中描述的名为 <code>useFriendStatus</code> 的自定义 Hook：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  // ...

  // 在开发者工具中的这个 Hook 旁边显示标签
  // e.g. &#34;FriendStatus: Online&#34;
  useDebugValue(isOnline ? &#39;Online&#39; : &#39;Offline&#39;);

  return isOnline;
}
</code></pre><p>React 不推荐你向每个自定义 Hook 添加 debug 值。当它作为共享库的一部分时才最有价值。</p>
<h4 id="延迟格式化-debug-值">延迟格式化 debug 值</h4>
<p>在某些情况下，格式化值的显示可能是一项开销很大的操作。除非需要检查 Hook，否则没有必要这么做。</p>
<p>因此，<code>useDebugValue 接受一个格式化函数作为可选的第二个参数</code>。<br />
该函数只有在 Hook 被检查时才会被调用。它接受 debug 值作为参数，并且会返回一个格式化的显示值。</p>
<p>例如，一个返回 Date 值的自定义 Hook 可以通过格式化函数来避免不必要的 toDateString 函数调用：</p>
<pre tabindex="0"><code class="language-base" data-lang="base">useDebugValue(date, date =&gt; date.toDateString());
</code></pre>
  </article>
</div>
      
<div class="grow"></div>
<nav class="pagination-single">
  
    
      <a href="https://codthing.github.io/react/redux/redux-base/" title="Redux" class="pagination-single__left">
        <div class="pagination-single__icon">
          <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path fill="currentColor" d="M19 11H7.83l4.88-4.88c.39-.39.39-1.03 0-1.42-.39-.39-1.02-.39-1.41 0l-6.59 6.59c-.39.39-.39 1.02 0 1.41l6.59 6.59c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L7.83 13H19c.55 0 1-.45 1-1s-.45-1-1-1z"/></svg>
        </div>
        <div class="pagination-single__right-title">Redux</div>
      </a>
    
    <div class="grow"></div>
    
      <a href="https://codthing.github.io/react/redux/hooks-redux/" title="Hooks &amp; Redux" class="pagination-single__right">
        <div class="pagination-single__left-title">Hooks &amp; Redux</div>
        <div class="pagination-single__icon">
          <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path fill="currentColor" d="M5 13h11.17l-4.88 4.88c-.39.39-.39 1.03 0 1.42.39.39 1.02.39 1.41 0l6.59-6.59c.39-.39.39-1.02 0-1.41l-6.58-6.6c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.02 0 1.41L16.17 11H5c-.55 0-1 .45-1 1s.45 1 1 1z"/></svg>
        </div>
      </a>
    
  
</nav>


      
  

    </article>
    
    <section id="list-side" class="r" data-dir="ltr">
      
        <div class="toc">
  <h6 class="toc__label">此页面上有什么</h6>
  <div class="toc__body">
    <nav id="TableOfContents">
  <ul>
    <li><a href="#一hooks-底层原理">一、Hooks 底层原理</a></li>
    <li><a href="#二hooks-规范">二、Hooks 规范</a>
      <ul>
        <li><a href="#1-只在最顶层使用-hook">1. 只在最顶层使用 Hook</a>
          <ul>
            <li></li>
          </ul>
        </li>
        <li><a href="#2-只在-react-函数中调用-hook">2. 只在 React 函数中调用 Hook</a></li>
      </ul>
    </li>
    <li><a href="#三自定义-hook">三、自定义 Hook</a>
      <ul>
        <li><a href="#1-提取自定义-hook">1. 提取自定义 Hook</a></li>
        <li><a href="#2-使用自定义-hook">2. 使用自定义 Hook</a></li>
        <li><a href="#3-在多个-hook-之间传递信息">3. 在多个 Hook 之间传递信息</a></li>
        <li><a href="#4-自定义hooks中使用usereducer">4. 自定义Hooks中使用useReducer</a></li>
        <li><a href="#5-异步-fetch-hooks">5. 异步 fetch Hooks</a>
          <ul>
            <li><a href="#1-封装-useasync">1). 封装 useAsync</a></li>
            <li><a href="#2-在组件中使用">2). 在组件中使用</a></li>
          </ul>
        </li>
      </ul>
    </li>
    <li><a href="#四eslint-插件">四、ESLint 插件</a></li>
    <li><a href="#五hooks-api">五、Hooks API</a>
      <ul>
        <li><a href="#1-usestate">1. useState</a>
          <ul>
            <li><a href="#1-普通式更新state">1). 普通式更新state</a></li>
            <li><a href="#2-函数式更新state">2). 函数式更新state</a></li>
            <li><a href="#3-合并更新对象">3). 合并更新对象</a></li>
            <li><a href="#4-惰性初始-state">4). 惰性初始 state</a></li>
            <li><a href="#5-跳过-state-更新">5). 跳过 state 更新</a></li>
          </ul>
        </li>
        <li><a href="#2-useeffect">2. useEffect</a>
          <ul>
            <li><a href="#1-清除-effect">1). 清除 effect</a></li>
            <li><a href="#2-effect-的执行时机">2). effect 的执行时机</a></li>
            <li><a href="#3-uselayouteffect">3). useLayoutEffect</a></li>
            <li><a href="#4-ssr-服务器端渲染">4). SSR 服务器端渲染</a></li>
            <li><a href="#5-effect-的条件执行1">5). effect 的条件执行1</a></li>
            <li><a href="#5-effect-的条件执行2">5). effect 的条件执行2</a></li>
          </ul>
        </li>
        <li><a href="#3-usecontext">3. useContext</a>
          <ul>
            <li></li>
            <li><a href="#使用方法">使用方法</a></li>
          </ul>
        </li>
        <li><a href="#4-usereducer">4. useReducer</a>
          <ul>
            <li><a href="#1-使用方法">1). 使用方法</a></li>
            <li><a href="#2-指定初始-state">2). 指定初始 state</a></li>
            <li><a href="#3-惰性初始化">3). 惰性初始化</a></li>
            <li><a href="#4-跳过-dispatch">4). 跳过 dispatch</a></li>
          </ul>
        </li>
        <li><a href="#5-usecallback">5. useCallback</a>
          <ul>
            <li></li>
            <li><a href="#1-usecallback的目的">1. useCallback()的目的</a></li>
            <li><a href="#2-usecallback的正确用例">2. useCallback的正确用例</a></li>
            <li><a href="#3-usecallback的糟糕用例">3. useCallback的糟糕用例</a></li>
            <li><a href="#4-总结所有性能优化的建议">4). 总结（所有性能优化的建议）</a></li>
          </ul>
        </li>
        <li><a href="#6-usememo">6. useMemo</a>
          <ul>
            <li><a href="#1-usememo-一个例子">1). useMemo() 一个例子</a></li>
            <li><a href="#2-usememo与usecallback">2). useMemo()与useCallback()</a></li>
            <li><a href="#3-小心使用记忆memoization">3). 小心使用记忆(memoization)</a></li>
            <li><a href="#4-结语">4). 结语</a></li>
          </ul>
        </li>
        <li><a href="#7-useref">7. useRef</a>
          <ul>
            <li><a href="#1-可变值">1). 可变值</a></li>
            <li><a href="#用例记录按钮点击">用例：记录按钮点击</a></li>
            <li><a href="#11-ref-和-state-的区别">1.1). ref 和 state 的区别</a></li>
            <li><a href="#用例实现秒表">用例：实现秒表</a></li>
            <li><a href="#2-访问-dom-元素">2). 访问 DOM 元素</a></li>
            <li><a href="#用例聚焦输入">用例：聚焦输入</a></li>
            <li><a href="#3-更新ref限制">3). 更新ref限制</a></li>
            <li><a href="#4-总结">4). 总结</a></li>
          </ul>
        </li>
        <li><a href="#8-useimperativehandle">8. useImperativeHandle</a></li>
        <li><a href="#9-usedebugvalue">9. useDebugValue</a>
          <ul>
            <li><a href="#延迟格式化-debug-值">延迟格式化 debug 值</a></li>
          </ul>
        </li>
      </ul>
    </li>
  </ul>
</nav>
  </div>
</div>
      
    </section>

  </div>
</div>

<script>
  'use strict';
  
  function wrap(el, wrapper) {
    el.parentNode.insertBefore(wrapper, el);
    wrapper.appendChild(el);
  }

  (function () {
    var singleContentsElem = document.querySelector('.single__contents');
    singleContentsElem ? 
    singleContentsElem.querySelectorAll('pre > code').forEach(function(elem) {
      var dataLang = elem.getAttribute('data-lang');
      var dataLangWrapper = document.createElement('div');
      var code = null;
      var codeTitle = null;

      if (dataLang && dataLang.includes(':')) {
        code = dataLang.split(':')[0];
        codeTitle = dataLang.split(':')[1];

        dataLangWrapper.className = 'language-' + code;
        dataLangWrapper.setAttribute('data-lang', codeTitle);

        elem.className = 'language-' + code;
        elem.setAttribute('data-lang', codeTitle);
        elem.setAttribute('id', codeTitle);
      }

      if (!dataLang || codeTitle) {
        wrap(elem.parentNode, dataLangWrapper);
      }

    }) : null;
  })();
  


  
  
  var dollarCodeElem = document.querySelectorAll('div.language-\\$');
  var gtCodeElem = document.querySelectorAll('div.language-\\>');
  
  dollarCodeElem ?
  dollarCodeElem.forEach(function(elem) {
    var lnts = elem.parentNode.parentNode ? elem.parentNode.parentNode.querySelectorAll('.lnt') : null;
    lnts ? 
    lnts.forEach(function(lnt) {
      lnt.innerHTML = '$<br/>';
    }) : null;
  }) : null;

  gtCodeElem ?
  gtCodeElem.forEach(function(elem) {
    var lnts = elem.parentNode.parentNode ? elem.parentNode.parentNode.querySelectorAll('.lnt') : null;
    lnts ? 
    lnts.forEach(function(lnt) {
      lnt.innerHTML = '><br/>';
    }) : null;
  }) : null;
  
</script>




<script defer src="/js/helper/getParents.min.js"></script>

<script defer src="/js/helper/closest.min.js"></script>

<script defer src="/js/helper/prev.min.js"></script>

<script defer src="/js/helper/prop.min.js"></script>

<script defer src="/js/helper/fadeinout.min.js"></script>












<script>
  document.addEventListener('DOMContentLoaded', function () {
    'use strict';
    
    
    var tables = document.querySelectorAll('.single__contents > table');
    for (let i = 0; i < tables.length; i++) {
      var table = tables[i];
      var wrapper = document.createElement('div');
      wrapper.className = 'table-wrapper';
      table.parentElement.replaceChild(wrapper, table);
      wrapper.appendChild(table);
    }
    


    
    
    
    var lib = JSON.parse("null");

    if (lib && lib.includes('mermaid')) {
      var themeVariant = localStorage.getItem('theme') || 'light';

      if (themeVariant === "dark") {
        mermaid.initialize({ theme: 'dark' });
      } else {
        mermaid.initialize({ theme: 'default' });
      }
      
      var mermaids = [];
      [].push.apply(mermaids, document.getElementsByClassName('language-mermaid'));
      mermaids.forEach(function(elem) {
        var elemParentNode = elem.parentNode;

        if (elemParentNode !== document.body) {
          elemParentNode.parentNode.insertBefore(elem, elemParentNode);
          elemParentNode.parentNode.removeChild(elemParentNode);
        }

        var newElemWrapper = document.createElement('div');
        newElemWrapper.classList.add('mermaid');
        newElemWrapper.innerHTML = elem.innerHTML;
        elem.replaceWith(newElemWrapper);
      });
    }
    
    

    
    if (lib && lib.includes('katex')) {
      var mathElements = document.getElementsByClassName('math');
      var options = {
        delimiters: [
          { left: "$$", right: "$$", display: true },
          { left: "\\[", right: "\\]", display: true },
          { left: "$", right: "$", display: false },
          { left: "\\(", right: "\\)", display: false }
        ],
      };

      renderMathInElement(document.querySelector('.single__contents'), options);
    }
    


    
    if (lib && lib.includes('flowchartjs')) {
      
      var options = JSON.parse("{\"arrow-end\":\"block\",\"element-color\":\"black\",\"fill\":\"white\",\"flowstate\":{\"approved\":{\"fill\":\"#58C4A3\",\"font-size\":12,\"no-text\":\"n/a\",\"yes-text\":\"APPROVED\"},\"current\":{\"fill\":\"yellow\",\"font-color\":\"red\",\"font-weight\":\"bold\"},\"future\":{\"fill\":\"#FFFF99\"},\"invalid\":{\"fill\":\"#444444\"},\"past\":{\"fill\":\"#CCCCCC\",\"font-size\":12},\"rejected\":{\"fill\":\"#C45879\",\"font-size\":12,\"no-text\":\"REJECTED\",\"yes-text\":\"n/a\"},\"request\":{\"fill\":\"blue\"}},\"font-color\":\"black\",\"font-size\":14,\"line-color\":\"black\",\"line-length\":50,\"line-width\":3,\"no-text\":\"no\",\"scale\":1,\"symbols\":{\"end\":{\"class\":\"end-element\"},\"start\":{\"element-color\":\"green\",\"fill\":\"yellow\",\"font-color\":\"red\"}},\"text-margin\":10,\"x\":0,\"y\":0,\"yes-text\":\"yes\"}");
      var jsonContent = null;

      var flowchartPrefix = "language-flowchart";
      var index = 0;
      Array.prototype.forEach.call(document.querySelectorAll("[class^=" + flowchartPrefix + "]"), function(x){
          x.style.display = 'none'
          x.parentNode.style.backgroundColor = "transparent"
          jsonContent = x.innerText;

          var node0 = document.createElement('div');
          node0.id = 'flowchart' + index;
          x.parentNode.insertBefore(node0, x);

          var diagram = flowchart.parse(jsonContent);
          diagram.drawSVG("flowchart"+index, options);

          index +=1;
      });      
    }
    


    
    if (lib && lib.includes('chart')) {
      var borderColor = "#666";
      var bgColor = "#ddd";
      var borderWidth = 2;

      Chart.defaults.global.elements.rectangle.borderWidth = borderWidth;
      Chart.defaults.global.elements.rectangle.borderColor = borderColor;
      Chart.defaults.global.elements.rectangle.backgroundColor = bgColor;

      Chart.defaults.global.elements.line.borderWidth = borderWidth;
      Chart.defaults.global.elements.line.borderColor = borderColor;
      Chart.defaults.global.elements.line.backgroundColor = bgColor;

      Chart.defaults.global.elements.point.borderWidth = borderWidth;
      Chart.defaults.global.elements.point.borderColor = borderColor;
      Chart.defaults.global.elements.point.backgroundColor = bgColor;

      var chartPrefix = "language-chart";
      var index = 0;
      var jsonContent = null;

      Array.prototype.forEach.call(document.querySelectorAll("[class^=" + chartPrefix + "]"), function (x) {
        x.style.display = 'none'
        x.parentNode.style.backgroundColor = "transparent"
        jsonContent = x.innerText;
        var node0 = document.createElement('canvas');
        var source = null;
        node0.height = 200;
        node0.style.height = 200;
        node0.id = 'myChart' + index;
        source = JSON.parse(jsonContent);
        x.parentNode.insertBefore(node0, x);
        var ctx = document.getElementById('myChart' + index).getContext('2d');
        var myChart = new Chart(ctx, source);
        index += 1;
      });            
    }
    


    
    if (lib && lib.includes('viz')) {
      var vizPrefix = "language-viz-";
      Array.prototype.forEach.call(document.querySelectorAll("[class^=" + vizPrefix + "]"), function (x) {
        x.style.display = 'none'
        x.parentNode.style.backgroundColor = "transparent"
        var engine;
        x.getAttribute("class").split(" ").forEach(function (cls) {
          if (cls.startsWith(vizPrefix)) {
            engine = cls.substr(vizPrefix.length);
          }
        });
        var viz = new Viz();
        viz.renderSVGElement(x.innerText, { engine: engine })
          .then(function (element) {
            element.style.width = "100%";
            x.parentNode.insertBefore(element, x);
          })
      });
    }
    
    
  });
</script>

<script>
  var listSide = document.getElementById('list-side');
  var listMain = document.getElementById('list-main');
  var singleMenu = document.getElementById('single-menu');
  var switchElem = document.querySelector('.switch');
  var wideViewAsDefault = JSON.parse("false");
  
  enquire.register("screen and (max-width:1280px)", {
    match: function () {
      if (wideViewAsDefault) {
        singleMenu.className = 'l';  
        listMain.className = 'mr';
        listSide.className = 'hide';
      } else {
        singleMenu.className = 'l';
        listMain.className = 'm';
        listSide.className = 'r';
      }
    },
    unmatch: function () {
      if (wideViewAsDefault) {
        singleMenu.className = 'l';
        listMain.className = 'mr';
        listSide.className = 'hide';
      } else {
        listSide.className = 'r';
        listMain.className = 'm';
        singleMenu.className = 'l';
      }
    },
  }).register("screen and (max-width:960px)", {
    match: function () {
      singleMenu.className = 'l';
      listMain.className = 'mr';
      listSide.className = 'hide';
      switchElem.className = 'hide';
    },
    unmatch: function () {
      if (wideViewAsDefault) {
        singleMenu.className = 'l';
        listMain.className = 'mr';
        listSide.className = 'hide';
      } else {
        singleMenu.className = 'l';
        listMain.className = 'm';
        listSide.className = 'r';
      }
      switchElem.className = 'switch';
    },
  }).register("screen and (max-width:600px)", {
    match: function () {
      listSide.className = 'hide';
      listMain.className = 'lmr';
      singleMenu.className = 'hide';
      switchElem.className = 'hide';
    },
    unmatch: function () {
      listSide.className = 'hide';
      listMain.className = 'mr';
      singleMenu.className = 'l';
      switchElem.className = 'hide';
    },
  });
</script>

        
<div class="bot">
  <footer class="footer">
    <div class="divider">
      <div class="lmr">
        
          <p class="caption">
            ©2022, codthing
          </p>
          
        
      </div>
    </div>
  </footer>
</div>

    </div>

</body>

</html>